display: tableの活用 第1回 フレキシブルな段組みレイアウト

2014年4月にWindows XPのサポートが終了し、業務で対応すべきブラウザがIE8もしくは、IE9以上に変化してきます。この記事ではIE8以降で使えるマルチカラムレイアウト手法を解説します。

発行

著者 小山田 晃浩 フロントエンド・エンジニア
display: tableの活用 シリーズの記事一覧

仕様は変更されていませんが、IEのサポートが終了した現時点ではCSS GridやFlexboxを使って、同様のレイアウトができます。「CSS GridとFlexboxの使い分け」などを参照してください。

はじめに

2014年4月9日にWindows XPのライフサイクルが終了し、普段の仕事でサポートをしなくてはいけないとされるブラウザは多くの場合、IE8以上、あるいはIE9以上へと前進したことでしょう。これにより、レイアウトの手法は大きく変わることになります。

本稿では、display: tableの挙動を解説し、あなたのCSSレイアウトテクニックを向上させることを目指します。

IE8はCSS2.1をフルサポートしています。CSS2.1の仕様内には、displayの値にtableがありますが、IE7までは利用することができませんでした。このdisplay: tableを使うと、今まで難しかったレイアウトをスマートに実現することができます。

絶対に崩れない、フレキシブルな段組み

CSSのdisplay: tableを応用することで段組が実現できます。同様のレイアウトは従来からfloatプロパティでも実現できました。しかし、floatにはなかった、display: tableならではの次の特長があります。

  • 絶対にカラム落ちしない
  • すべてのカラムの高さは、一番縦長と同じ高さになる

HTML

次のHTMLコードで段組が実現できます。.blockの中に段組み部分となる3つの.block__elementが配置されています。

<div class="container">
  先行するコンテンツ...

  <div class="block">
    <div class="block__element">
      <img src="img_01.png" alt="">
      見出しやテキストとテキスト
    </div>

    <div class="block__element">
      <img src="img_02.png" alt="">
      見出しやテキストとテキスト
    </div>

    <div class="block__element">
      <img src="img_03.png" alt="">
      見出しやテキストとテキストとテキストとテキストと...
    </div>
  </div>

  後続するコンテンツ...
</div>

CSS

CSSについては、次の2点に注目してください。

  • 全カラムを包含するコンテナーにdisplay: table;を適用する
  • 各カラムとなる.block__elementにはdisplay: table-cell;と横幅を適用する

次のCSSは主要な部分だけを抜粋しています(以下同)。

.container{
  width: 450px;
  margin: 0 auto;
}
.block{
  display: table;
  margin: 20px 0;
}
.block__element{
  display: table-cell;
  width: 130px;
  padding: 9px;
  border: 1px solid #666;
}

上記の例では、3カラムですが、2カラムや4カラム以上であっても同じ方法で実現できます。とはいえ、この程度でしたらfloatプロパティでも実現可能でしょう。

縦に伸びる特性

段組をする際、a要素のクリックできる範囲が、カラムによって異なる挙動をしばしば見かけます。しかし、display: table;を応用すれば、すべてのカラムの高さが揃うのでこういった、不自然な挙動を簡単に自然にすることができます。カラムの高さ統一によって、a要素のクリック範囲も統一されています。

HTML

HTMLは先ほどのサンプルとほとんど変わりがありません。必要な箇所をaタグで括っただけの違いです。

<div class="container">
  先行するコンテンツ...

  <div class="block">

    <div class="block__element">
      <img src="img_01.png" alt="">
      ここはリンクじゃないよ
    </div>

    <a href="#">
      <div class="block__element">
        <img src="img_02.png" alt="">
        内容が少ないリンク
      </div>
    </a>

    <a href="#">
      <div class="block__element">
        <img src="img_03.png" alt="">
        内容が多いリンク内容が多いリンク内容が多いリンク内容が多いリンク
      </div>
    </a>
  </div>

  後続するコンテンツ...
</div>

CSS

子要素セレクタを活用し、.blockの直下に配置される要素に対して、table-cellが適用されるようにします。

.container{
  width: 450px;
  margin: 0 auto;
}
.block{
  display: table;
  margin: 20px 0;
}
.block > .block__element,
.block > a{
  display: table-cell;
  width: 130px;
  padding: 9px;
  border: 1px solid #666;
}
.block > a:hover{
  background: #dd3;
}

カラム内の天地中央揃え

table-cellでレイアウトされたカラムは、vertical-alignが有効です。ですので、カラム間で、天地を中央に揃える(vertical-align: middle;)ことができます。

HTML

インラインのボックス、ブロックのボックス問わず、table-cell直下の要素を天地揃えをコントロールすることができます。

<div class="container">
  先行するコンテンツ...

  <div class="block">
    <div class="block__element">
      <img src="long.png" alt="">
    </div>

    <div class="block__element">
      <div style="background: #09C;">divボックス</div>
    </div>

    <div class="block__element">
      インライン<br>テキスト
    </div>
  </div>

  後続するコンテンツ...
</div>

CSS

table-cellと合わせてvertical-align: middle;を適用します。

.container{
  width: 450px;
  margin: 0 auto;
}
.block{
  display: table;
  margin: 20px 0;
}
.block{
  display: table;
  margin: 20px 0;
}
.block__element{
  vertical-align: middle;
  display: table-cell;
  width: 130px;
  padding: 9px;
  border: 1px solid #666;
}

もちろんvertical-alignの値は、middle以外(topbottomなど)も適用可能です。

横に伸縮する特性と入れ子

display: table-cell;は横に伸び縮みします。この特性を利用すれば、「入れ子」や、レスポンシブなブロックをスマートに実装できます。

Webサイトを作る際、ブロックの「入れ子」はしばしば発生します。例えば、同じブロック(コンポーネント)が、異なる横幅のコンテナー内で利用されるケースです。その解決策として、display: table;を使えば、1コードでさまざまな横幅のケースに対応することができます。

display: table;による多段カラムは、フレキシブルに横方向に伸縮させることができます。そして、縮めてもfloatのレイアウトように崩れることはありません。

HTML

幅広配下、幅狭配下それぞれの.blockのHTML構造は、まったく同じであることに注目してください。同じ.blockの横幅は自動で伸縮することになります。

<div class="container">
  先行するコンテンツ...

  <div class="block">

    <div class="block__avator">
      <img src="img_01.png" alt="">
    </div>

    <div class="block__text">
      ここには少し長いテキストを入れたりします。ここには少し長いテキストを入れたりします。
    </div>

    <div class="block__control">
      <a href="#" class="button">操作ボタン</a>
    </div>

  <!-- /.block --></div>

  <div class="narrow">
    幅狭の中で先行するコンテンツ...

    <div class="block">

      <div class="block__avator">
        <img src="img_01.png" alt="">
      </div>

      <div class="block__text">
        ここには少し長いテキストを入れたりします。ここには少し長いテキストを入れたりします。
      </div>

      <div class="block__control">
        <a href="#" class="button">操作ボタン</a>
      </div>

    <!-- /.block --></div>

    幅狭の中で後続するコンテンツ...
  </div>

  後続するコンテンツ...
</div>

CSS

CSSのポイントは以下の4点です。

  • ブロック全体幅を100%にする(外からカラム郡全体の幅を指定する)
  • widthborderの同時指定により、親以上の幅にならないようにbox-sizingを指定する
  • 任意のカラムの横幅を明示する(内から任意カラムの幅を指定する)
  • 可変させてもいいカラムには横幅を明示しない
.block{
  display: table;
  box-sizing: border-box;
  width: 100%;
  margin: 20px 0;
  border: 1px solid #666;
}
.block .block__avator{
  vertical-align: top;
  display: table-cell;
  width: 130px; /* 固定させたいのでwidthを明示 */
}
.block .block__text{
  /* 可変させてもいいのでwidthを付けない */
  vertical-align: top;
  display: table-cell;
}
.block .block__control{
  vertical-align: top;
  display: table-cell;
  width: 130px; /* 固定させたいのでwidthを明示 */
}

横に伸びるデメリットの制御

これまで解説してきたとおり、display: table;で実現した多段カラムは、floatのようにカラム落ちすることはなく、代わりに横幅が伸縮します。これはデメリットにもなりえます。

URLのような英数字のみで構成された長い文字列は、自動折り返しされません。折り返ししないなどの理由で、横長のボックスが格納されてしまう場合、意図しない自動伸縮が発生してしまいます。

見ての通り、親からはみ出るだけでなく隣のカラムを圧迫します。

これを防ぐためにはoverflow: hidden;を設定すれば回避できると想像される方もいるかもしれません。しかしながら、table-cellにはそれが通用せず、強制的に収まるサイズまで伸びてしまうのです。

伸びてしまう問題を回避するためには、table全体の幅を明示するとともに、table-layout: fixed;を指定します。これでコンテンツ内容により、ほかのカラムの圧迫を阻止し、意図しない崩れが発生する可能性を回避できます。

横幅とtable-layout: fixed;を明示しておくことで、はじめてoverflow: hidden;を発動させることができます。テキストの折り返しを強制的に行いたい場合には、word-wrap: break-word;を合わせて指定しておきます。

この実装により、はみ出した画像は見切れ、折り返し不能だったテキストは強制的に折り返します(一昔前は、Firefoxで意図したとおりに動きませんでしたが、現在は解決しています)。

HTML

class属性値が.block__element2のdiv要素の中には、長いテキストや、横長の画像が格納されています。

<div class="container">
  先行するコンテンツ...

  <div class="block">

    <div class="block__element1">
      ここにはテキストを入れるよ
    </div>

    <div class="block__element2">
      http://longlonglonglonglonglonglonglonglongURL.com/
      <img src="horizontal.png" alt="">
    </div>
  </div>

  後続するコンテンツ...
</div>

CSS

.blocktable-layout: fixed;width: 100%;を指定します。

長いテキストや、大きな画像が入る可能性があるカラムには、word-wrap: break-word;overflow: hidden;を指定します。

.block{
  display: table;
  table-layout: fixed;
  box-sizing: border-box;
  width: 100%;
  margin: 20px 0;
}
.block__element1{
  display: table-cell;
  width: 130px;
  padding: 9px;
  border: 1px solid #ccc;
}
.block__element2{
  display: table-cell;
  border: 1px solid #ccc;
  word-wrap: break-word;
  overflow: hidden;
}

まとめ

IE6、7という呪縛を断ち切ることで、display: table;によるレイアウトを業務で使うことができるようになりました。

table表示によるレイアウトは、崩れにくいという固さとフレキシブルに伸縮するという柔軟さを併せ持っており、複雑なコンポーネントの組み合わせや、マルチデバイス対応といった、いまどきのCSS設計に求められる悩みの多くを解決します。

display: table;の特性はほかにも使い道があります。次回はさらなる応用例を解説します。