KOJIKA17

floatを解除する手法のclearfix と 次世代のレイアウトの話

2011年にclearfixについて記事を書きましたが、Micro clearfixなどの新しい手法も出てきました。
記事を書いてから2年が経ち、色々思うところも出てきたので、改めてclearfixについて書きます。

floatを解除するには

CSSでレイアウトの構築を行う際に、よくfloatプロパティが用いられます。
しかしfloatを使用すると、「親の背景が表示されない」「下の段のレイアウトが崩れる」などのトラブルが起こりやすくなります。

floatプロパティの特性

親の背景が表示されない理由は、floatプロパティを指定しているボックスが浮動化し、通常のボックスのフローから外されることが原因です。
通常フローから外されたボックスは、親のボックスの高さを認識できなくなり、その結果、高さによって描写領域が決まる背景が表示されないという状態になります。
またfloatプロパティは、一定方向に寄せて後続する内容を流し込むという特性があり、これよって意図しない後続のボックスまで流し込まれてレイアウトが崩れる場合があります。

この「浮動化」と「後続する内容の流し込み」という2つの特性によって、悩まされている人も多いのではないでしょうか。

親のボックスの高さが出ない例

<style>
#wrapper {
  padding: 10px;
  background: #aaa;
}
#main {
  float: left;
  width: 60%;
  height: 200px;
  background: #5ce;
}
#side {
  float: right;
  width: 36%;
  height: 200px;
  background: #5ce; 
}
</style>

<div id="wrapper">
  <div id="main"></div>
  <div id="side"></div>
</div>

clearプロパティについて

floatの特性を解消するには、clearプロパティを使用します。
このプロパティを浮動化したボックスの後ろに使用すると、一般的に「floatを解除した」と呼ばれる状態になり、「後続する内容の流し込み」が解除されます。

floatプロパティをclearプロパティが解除する原理は、clearプロパティを適用したボックスにクリアランスというものが導入されます。このクリアランスは、margin-topプロパティを自動的に浮動化されたボックスの高さまで調整するような振る舞いをします。
「floatを解除する」というよりも、「floatの影響の及ばない範囲まで調整する」というニュアンスが良いかもしれません。

なおclearプロパティとmargin-topプロパティが同時に指定されている場合は、clearプロパティが優先されます。(IE6 と 兄弟に通常フローのボックスがある場合を除く)

親のボックスの高さを出す

<style>
#wrapper {
  padding: 10px;
  background: #aaa;
}
#main {
  float: left;
  width: 60%;
  height: 200px;
  background: #5ce;
}
#side {
  float: right;
  width: 36%;
  height: 200px;
  background: #5ce;
}
.clear {
  clear: both;
  height: 60px;
  background: #e58;
}
</style>

<div id="wrapper">
  <div id="main"></div>
  <div id="side"></div>
  <div class="clear"></div>
</div>

しかしclearプロパティは浮動化したボックスの後ろに指定しなければなりません。そのため状況によっては「不要なHTML要素が増える」、または「HTMLの文法に問題が生じる」場合があります。
その問題を解決する方法が、clearfixと呼ばれる手法です。

clearfixとは

clearfixを最初に考案したのは、オーストラリア人のWebデベロッパー Tony Aslettという方が、2004年5月8日に擬似要素を使用した手法を公表したのが、clearfixのはじまりです。
しかしこの時は、IEに対して完全な対応ができていなかったらしく、 2004年12月13日にIE5にも対応した、オリジナルとなるclearfixが再び公表されます。

「.clearfix」というクラスを、floatプロパティを使用している親の要素に指定して使います。

clearfix(オリジナル)
.floatcontainer:after{
  content: "."; 
  display: block; 
  height: 0; 
  font-size:0;	
  clear: both; 
  visibility:hidden;
}
	
.floatcontainer{display: inline-block;} 

/* Hides from IE Mac \*/
* html .floatcontainer {height: 1%;}
.floatcontainer{display:block;}
/* End Hack */ 

clearfixという手法は、擬似要素の:afterとIEの仕様やバグを利用したものです。
擬似要素の:afterはcontentプロパティと一緒に使うことで、ボックス内の末尾に、新たなインラインのボックスを生成します。

末尾にインラインの要素が生成される

:afterによって生成されたボックスに、display: block;とclearプロパティを設定することで、div.clearと同じ状態になります。

生成されたボックスに、display: block;を指定

contentプロパティの値に何も指定せずに、空のボックスを生成すれば、height: 0; や font-size:0; などの記述は必要ありませんでした。
しかしNetscapeブラウザのバグに、「contentプロパティに何らかの文字が入っていないとボックスが生成されない」という問題があります。このバグの対応に、目立ちにくい文字である".(ドット)"が指定されています。

また、IE7以前のブラウザは擬似要素の:afterが使用できないため、hasLayoutというIEの独自機能を有効化するために、display: inline-block; などのIE向けのハックが利用されています。

micro clearfix

2011年4月21日にNicolas Gallagherという方が、A new micro clearfix hackとして記事を投稿しました。
この手法は、IE5やNetscapeなどのほとんど使われなくなった古いブラウザを切り捨て、CSSをコンパクトにしたものです。

.cf:before,
.cf:after {
    content: " ";
    display: table;
}

.cf:after {
    clear: both;
}

.cf {
    *zoom: 1;
}

オリジナルのclearfixに比べると、非常にコンパクトになっています。

micro clearfixの解説と再考

micro clearfixは、IE6以上やモダンブラウザを意識して作成されました。
オリジナルのclearfixと共に、micro clearfixを見てみましょう。

clearfixのcontent: "."; の指定

オリジナルのclearfixでは、contentプロパティに ".(ドット)" が指定されており、この影響によってレイアウト上に".(ドット)"が出現してしまいます。 ".(ドット)"を非表示するには、heightやvisibilityプロパティを指定しなければならず、冗長的なコードになっていました。
このcontentプロパティに値は、Netscapeブラウザのバグ回避のための指定であり、本来は空でも問題ない値です。

ただmicro clearfixでは、contentプロパティの値には" (スペース)"が指定されています。Netscapeブラウザを使用しているユーザーも少なくなっている現状と、当時のOperaのバグのためにスペースを指定しているようです。
現在、Operaはこのバグが解消されているため、contentプロパティの値を空にしても問題ないでしょう。

micro clearfixの:before

micro clearfixで特徴的なのは、疑似要素による:beforeの指定でしょう。
:beforeを使用した意図は、micro clearfixの記事に書かれています。

Including the :before selector is not necessary to clear the floats, but it prevents top-margins from collapsing in modern browsers. This has two benefits:

  1. It ensures visual consistency with other float containment techniques that create a new block formatting context, e.g., overflow:hidden
  2. It ensures visual consistency with IE 6/7 when zoom:1 is applied.

説明にもあるように、:beforeはfloatを解除する目的ではなく、marginの相殺を防ぐために設定されています。:beforeを指定することで、モダンブラウザとIE6/7の一貫性の確保できるなどの利点があります。

floatを使用した場合、ボックスは通常フローから外れるため、marginの相殺は発生しません。

しかし、clearfixを指定した子の要素が通常フローにあり、かつmargin-topが指定されている場合、モダンブラウザにおいてmarginの相殺が発生します。
IE6/7の場合は、モダンブラウザと同じソースでも、clearfixによってhasLayoutが有効になっています。
このhasLayoutの影響によってmarginの相殺が発生せず、モダンブラウザとの見た目が変わってしまいます。

IE6/7 と IE8以上のモダンブラウザの見え方の違い

.clearfix {
  background: #f00;
  *zoom: 1;
}
.clearfix:after {
  content: '';
  display: table;
  clear: both;
}
.box1,
.box2 {
  float: left;
  background: #eee;
  width: 240px;
  height: 80px;
  margin: 24px;
}
.box2 {
  float: none;
  margin: 60px 0 0 280px;
}

IE8やモダンブラウザの見え方

IE6/7の見え方。同じソースでも異なる

clearfixのコンセプトである「floatを解除する」という点において、:beforeは必要ありません。
しかしIE6/7の対応や、clearfixの中にfloatを指定しない要素を頻繁に入れる場合は、:beforeを設定した方が良いかもしれません。

clearfix の display

冒頭でもclearプロパティの説明を行いましたが、clearプロパティはmargin-topプロパティを、floatされたボックスの高さまで調整し、「floatが解除された」ように見せているだけです。
理論的に、margin-topを指定しても問題ないdisplayプロパティの値であれば、何でも構いません。

もし:beforeを設定し、marginの相殺を防止するのであれば、display: table;。
floatを解除する機能だけ欲しいのであれば、displayプロパティの値には、blockやtableのどちらかを指定すれば良いでしょう。

micro clearfix よりも、もっとコンパクトな clearfix の提案

IE6, IE7のブラウザのシェアは、この記事を書いている時点で、それぞれ6.27%と1.9%です。(Net Applications 調べ 2013年5月現在)
IE7以下のブラウザのシェア率も低くなってきたので、これらのブラウザを除外した形で再考しました。

.clearfix:after {
  content: "";
  clear: both;
  display: block;
}

floatプロパティを解除することだけに特化しているため、コンパクトになりました。
もしIE6/7の対応や、marginの相殺が気になる人は、micro clearfixを利用すると良いでしょう。

overflow

floatを解除する方法は、clearfixだけではありません。
overflow: hidden;を使用する方法もあります。

この方法は、overflowプロパティでvisibleキーワード以外を指定すると、新しいブロックの書式コンテキストを確立することできます。ブロックが確立されると、floatプロパティの指定された高さも認識してくれるようになり、親のボックス高さを算出されるようになります。

しかしIE6/7では、overflowプロパティが効きません。
hasLayout (zoom: 1;など)を有効化することで、IE6/7もfloatを解除できるようになります。

overflowプロパティを使用して、親のボックス高さを有効化させる例

.parent {
  overflow: hidden;
  zoom: 1;
}
.child {
  float: left;
}

clearfixに代わる仕様

clearfixはコンパクトにしたとしても冗長的な印象を受けます。

clearfixに代わる仕様として、Editer's Draftに上がっている CSS basic box modelclear-after というプロパティが提案されています。
この仕様は、2013年4月4日に約10年ぶりに仕様が更新されました。もしこの仕様が実現すれば、以下のように非常にコンパクトな指定が可能になります。

.clearfix {
  clear-after: left;
}

/* もしくは、clearプロパティにキーワードの複数指定 */
.clearfix {
  clear: left after;
}

ただ残念なことに現在は、このプロパティを実装しているブラウザはありません。この仕様が勧告される頃には、floatプロパティを使用してレイアウトを組み立てる以前に、次世代のレイアウト手法が用いられているかもしません。

次世代のレイアウト手法

clear-afterプロパティを紹介しましたが、先の長い話です。
またclearfixやoverflowを使用した手法も万能ではありません。clearfixは擬似要素が占有されますし、overflowもボックスから子の要素をはみ出したい場合や印刷において問題を持っています。

現在、CSS3では、新しいレイアウトの仕様が策定されています。

特にこの中でもFlexible Box Moduleは、floatに代わるレイアウトの方法として個人的に注目しています。
floatのように、横に並べるのはもちろんのこと、ボックスの並べ替え、縦の中央揃えや等間隔配置などが仕様で用意されています。

Flexible Box Moduleの概要を掴むには、Microsoftが提供している Internet Explorer 10の可変ボックス ("Flexbox") レイアウト (Windows)や、OperaのAdvanced cross-browser flexboxの資料が分かりやすいでしょう。

しかし注意が必要です。
過去にFirefoxやwebkitが実装していたdisplay: box;の仕様とは異なっています。
またIE10が実装しているのは、2012年3月22日付のCSS Flexible Box Layout Moduleの草案に基づいており、最新の仕様である2012年9月18日付のFlexible Box Layout Moduleの勧告候補とは異なります。

2012年3月22日 と 2012年9月18日 の Flexible Box Layout Module の比較

2012年3月22日付 2012年9月18日付
display: flexbox display: flex
flex-line-pack align-content
flex-align align-items
flex-item-align align-self
flex-pack: start justify-content: flex-start

Flexible Box Moduleは過去に大きな仕様変更を繰り返しているため、[display: box;] [display: flex-box;] [display: flex;]のプロパティと値の仕様がそれぞれ変更されています。その結果、ブラウザのバージョンによって実装が異なり、現状はカオスとしかいいようがありません。
もしdisplay: box;だけを使い続けている方がいたら、最新の仕様に目を通すことを強くお奨めします。

Flexible Box Module以外の仕様は、これほど大きな仕様変更は繰り返していませんが、ブラウザの実装環境にバラつきがあります。
IE10が現在のIE6の座に来るまでは、次世代のレイアウトを実践で扱うには、まだまだ難しいといえるでしょう。

floatプロパティ と 次世代のレイアウトについて

clearプロパティから始まり、clearfixやoverflowプロパティ、そして後半は次世代のレイアウト技術について触れました。
floatプロパティは、長く使用されてきた技術ですが、新しい仕様によってfloatプロパティが拡張される話もあります。さらに多くのブラウザでも利用できるプロパティですし、これからも使われていくことでしょう。
ボックスが通常のフローから外れることを意識すれば、floatは難しくないプロパティです。
使いこなせば、clearfixなどの「float解除」を行わずに、短いソースでレイアウトを構築することもできます。

またfloatプロパティ以外にも、次世代のレイアウトが行えるようになったとしても、万能の方法はきっとないでしょう。
手法に囚われることよりも、CSSプロパティの特性やアプローチの仕方を理解する事の方が重要な気がします。