解 説

HTML&CSSのコーディングで初心者泣かせなのがフロートを使ったレイアウトです。その理由は正しいfloatプロパティの原理原則を勉強してない事と、clearプロパティの正しい使い方を理解してないからです。
また、clearプロパティよりももっと簡単に使えるoverflowで解決する方法については、その仕組みがどこにもちゃんと解説されてないため、おまじない的な使い方という印象が拭いきれずに躊躇するためではないでしょうか。
今回はなぜoverflowを使うとフロート解除ができるのかその理由を解説しますが、まずfloatとclearの原理を簡単に復習しておきます。

floatの仕組み

floatプロパティは値を「left」にすると左に配置され、「right」にすると右に配置されると単純に理解していると使いこなせません。正しい動きを理解しておきましょう。

floatの仕組みはCSSの仕様書の視覚整形モデル9.5 浮動のところに書いてあります。

また、フロートの仕組みを正しく理解するには包含ブロックの考え方を知っておく必要があります。まずは、包含ブロックについて調べておきましょう。
包含ブロックについての説明はこちら

視覚整形モデルで記述されているfloatプロパティは次のような仕様です。

浮動体の配置:’float’プロパティ
プロパティ名: float
値: left | right | none | inherit
初期値: none
適用要素: すべて、ただし9.7節参照
継承: no
パーセンテージ: 利用不可
メディア: visual
算出値: 指定される

フロートの仕様で主要なポイント

1,浮遊について
floatプロパティを「none以外」の値に設定するとその要素は浮きだした状態となり、包含ブロック(親要素)はフロートした要素を見失います。
これはフロートした要素は今までのフローから抜け出すことを意味しています。
この時、包含ブロック(親要素)はフロートした要素の高さがわからなくなります。
2,フロートする範囲について
フロートは包含ブロック(親要素)の横幅の範囲内で動く事ができて、包含ブロックの横幅から飛び出すことはありません。上方向についても包含ブロックの上辺を飛び出すことはできません。包含ブロック(親要素)の下辺はコンテンツのサイズに依存しますので、子要素がフロートした段階で通常フローから抜け出していますので包含ブロック(親要素)はフロートした子要素が無くなったかのように囲むことをやめてしまいます。
全ての子要素がフロートした場合は包含ブロック(親要素)の高さは0になります。この状態がフロートした子要素を囲めない状態です。
もしフロートした要素の兄弟要素の中でfloatプロパティの値がnoneで通常フローのままの要素があったら、親要素はその子要素の高さだけを認知してその要素の高さまで囲むようになります。
3,位置
フロートした要素はまず浮いた状態になり、その後包含ブロック内でできるだけ上に行こうとします。
これ以上、上にいけなくなると、次にfloatプロパティで指定された値「left」または「right」に従って「left」なら包含ブロックの左端に、「right」なら右端に移動します。
4,他の要素への影響
フロートした要素の下に配置した他の要素の影響は、フロートした要素は今までのフローから抜け出しますので、包含ブロック(親要素)はフロートした要素を囲めない状態となり、高さは0となります。
下に配置した要素はフロートした要素が無くなったかのように上に上がって来ます。
ただし、行ブロックだけはフロートした要素を避けるように配置されます。
5,複数のフロート要素がある時の動き
複数のフロート要素がある場合はHTML上で上に記述されたものから順番に並びます。floatプロパティで指定された値が「left」なら包含ブロックのできるだけ上に行きその後、包含ブロックの左端から右に向かって横並びになります。「right」の場合はその逆の並びになります。
横並びになって、もし包含ブロック(親要素)の横幅に入りきれない場合はカラム落ちして、最初に横並びになったフロート要素の下から再び横並びになります。
複数のフロートした要素同士が重なることはありません。

フロートの例

親要素「.wrap」と、子要素「.row1」、子要素「.row2」に横幅(width)を設定しました。
子要素「.row1」、子要素「.row2」は通常フローしており、親要素「.wrapx」は子要素「.row1」、子要素「.row2」を囲んだ状態です。
表示結果

通常フローのサンプル
HTMLコード

CSSコード

上述の「フロートの仕様で主要なポイント」の実例を上記サンプルで確認してみましょう。

1,浮遊についてと4,他の要素への影響の例

「.row1」に「float:left」を指定したサンプル
「.row1」に「float:left」を指定。
「.row1」はまず浮いた状態になり「.row1」はできるだけ上に行こうとして動きますが、「.wrap」の上辺より上のh1領域には入れません。そのため今度は左に動いて「.wrap」の左辺に付いています。「.row2」は通常フローの状態です。「.row1」が浮いた状態、つまりフローから外れた瞬間に親要素は「.row1」の高さがわからなくなり、親要素は「.row1」が無くなったかのように「.row2」だけを囲むようになります。また、「.row2」も「.row1」が無くなったかのようにできるだけ上に向かいます。上がれる場所は親要素の上辺と同じ位置までです。
そして面白いことに「.row2」のテキスト(行ボックス)はフロートした「.row1」を避けるように回り込みをします。ここで注意すべき点は、テキストは「.row1」を避けるように動きますが、「.row2」の領域つまり背景色がピンク色の長方形領域は「.row1」の背景に潜り込んでいることです。

2,フロートする範囲についての例

「.row1」と「.row2」にそれぞれ「float:left」を指定サンプル
「.row1」と「.row2」にそれぞれ「float:left」を指定。
2つの要素はフロートしますが親要素「.wrap」の範囲内で並びます。親要素の範囲から飛び出すことはないです。

「.row1」と「.row2」にそれぞれ「float:left」を追加したCSSコード

表示結果

3,位置 と 5,複数のフロート要素がある時の動き

CSSの設定と結果は上記1のCSS設定、結果画像と同様です。
「.row1」と「.row2」共に「float:left」を指定しましたので、まず「.row1」ができるだけ上に行こうとして動きますが、「.wrap」の上辺より上のh1領域には入れません。そのため今度は左に動いて「.wrap」の左辺に付いています。次に「.row2」ができるだけ上に行こうとして動きますが、「.wrap」の上辺より上のh1領域には入れません。そのため今度は左に動いて先に動いた「.row1」の左辺に付きます。

複数のフロート要素がある場合もこの例で分かります。「.row1」と「.row2」HTML上で上に記述されたものから順番に並びます。
floatプロパティで指定された値が「left」なので、「.row1」は包含ブロックのできるだけ上に行きその後、包含ブロックの左端に付きます。その後「.row2」も同様に包含ブロックのできるだけ上に行きその後、包含ブロックの左端に移動しようとしますが、「.row1」があるため包含ブロックの左端にたどり着けずに「.row1」の右端に付いて横並びになります。もし包含ブロック(親要素)の横幅に入りきれない場合、つまり「.row1」と「.row2」の横幅の合計が「.wrap」の横幅より大きい場合はカラム落ちして、最初に横並びになったフロート要素「.row1」の下に「.row2」が配置されます。
表示結果

その他フロートの挙動を示したサンプルは「サンプル置き場」の「CSSを理解しましょう。動くモデルで紹介しました」にあります。

clearプロパティの仕組みと使い方

視覚整形モデルで記述されているclearプロパティは次のような仕様です。

9.5.2 浮動体に隣接するフローの制御:’clear’プロパティ
プロパティ名: clear
値: none | left | right | both | inherit
初期値: none
適用要素: ブロックレベル要素
継承: no
パーセンテージ: 利用不可
メディア: visual
算出値: 指定される

clearプロパティはdisplayプロパティが「block」、「list-item」、「table」に設定されている要素で設定します。

clearプロパティはfloatした結果崩れたレイアウトを元の状態にするための魔法のような仕組みではありません。
clearプロパティはこれを指定した要素が自分より上側でフロートした要素よりも下に位置なるようにクリアランスという特殊なマージンを作るものです。
clearプロパティを指定しないと、ある要素がフロートするとその下に位置する要素はフロートした要素がフローから抜けるため、フロートした要素が無いものとしてフロートした要素の背後をすり抜けてフロートした要素の上の要素のところまで上がって来ます。
ここでclearプロパティを「both」などの値にすることでフロートした要素の下になるようにクリアランスマージンを作ってフロートした要素の下に位置します。

clearプロパティを使用したサンプル

このクリアランスマージンとこの要素に指定したmargin-topの値は相殺します。
そのため大きなクリアランスマージンが入っている場合には、margin-topは全く作用しません。

どうしても必要な場合はクリアランマージン以上の値を設定しなければなりませんので通常はclearプロパティを設定した要素のmargin-topの値は使用しません。この場合フロートした要素のmargin-bottomの値で調整します。

clearプロパティは初心者にとって設定する要素を決めることが難しいかもしれません。
ポイントはフロートしている要素と兄弟になる後続要素に設定する事です。けれどもうまいことそのような候補になる要素が存在しないことも非常に多いです。
また、フロートしている要素の包含ボックス(親要素)を超えて、親要素と兄弟になる要素にclearプロパティを付けてしまうミスです。
この場合は親要素を無視して親と親の兄弟間でクリアランスが作られます。一見レイアウト崩れを解決したように見えますが、フロートした要素の親要素が、フロートした要素を囲めない問題は解決されていません。
このようなことから初心者にとってフロートレイアウトは難しいとなるのです。

overflowプロパティでclearプロパティと同等の処理を行う仕組み

overflowプロパティの仕様は11 視覚効果に記述されています。

視覚効果で記述されているoverflowプロパティは次のような仕様です。

11.1.1 はみ出し:’overflow’プロパティ
プロパティ名: overflow
値: visible | hidden | scroll | auto | inherit
初期値: visible
適用要素: 整形コンテキストを設置するブロックコンテナおよびボックス
継承: no
パーセンテージ: 利用不可
メディア: visual
算出値: 指定される

overflow:hiddenを使用したサンプル
overflow:hiddenは本来オーバーフローした内容を隠すためのプロパティと値です。それがなぜフロートと関連しているのかよくわからないため、使用に戸惑うこともあると思います。
overflowプロパティでclearプロパティと同等の処理を行えるのは、本来の仕組みとは別にoverflowプロパティには別の側面があるからです。それはoverflow プロパティをデフォルト値(visible)以外の値にすると、新たなブロック整形文脈を作るという機能を持ちわせていることです。
ブロック整形文脈とはブロックボックスのレイアウトを作る基準になれることです。わかりやすく言うとoverflow プロパティをデフォルト値(visible)以外の値にすると自分の範囲内に存在するフロートした要素を認識してその高さを知り、囲むことができるようになります。
この動きは適正にclearプロパティを設定した場合と同じ効果を生み出します。

ブロック整形文脈に付いてははCSS仕様書の中の視覚整形モデルの「9.4.1 ブロック整形コンテキスト」の項目に記述されています。

overflowプロパティでfloatのレイアウト崩れを解決する方法は、フロートした要素の親さえ見つけ出せれば簡単に設定できますので初心者でも無理なくfloatレイアウトに親しむことができると思います。まず、この方法を身につけてCSSに慣れてくるに従って、clearプロパティを適正に使う場面があったならclearプロパティを使うことでその使い方に慣れていけば良いでしょう。

overflowプロパティを使ってfloatを解決する方法が嫌われる理由として、overflowプロパティの値をhiddenとすることでオーバーフローした内容を隠してしまいます。そのためデザインになんらか問題がでる可能性があるということです。
けれども、現実的にはフロートした要素がオーバーフローすることは滅多にないことではないでしょうか。
それは、まずオーバーフローするということはフロートする要素が包含ブロック(親要素)より大きいことを意味します。横幅で考えると、そもそも親要素を飛び出すほどのサイズの子要素をフロートする意味がありません。なぜならフロートはあくまで親要素の範囲内で行われるものだからです。しかし、画像がオーバーフローするなど現実的には起きたりしますが、それは明らかにfloat以外の別の方法でレイアウトすべきものだと思います。
もしありうることとしたら高さの問題です。親要素の高さを指定して固定した場合、フロートした要素の内容(テキストの量)が増えると当然オーバーフローします。このような場合に問題が起こります。
けれどもそもそもデザイン的な理由で高さを固定すべきではありませんし、どうしてもやむ終えない場合はoverflowプロパティの値をhiddenではなくscrollにすれば良いことになります。そしてどうしてもこのようなことが起こってしまった場合のみ下で紹介するclearfixを活用すると良いでしょう。

もう一つ古いIE対策の問題がありますが、今ではこれらはもう無視すべきでしょう。

また、Firefoxで印刷する場合overflowを使うとレイアウト崩れる現象があると言われていますが、他原因に起因する不具合も見られます。これはmedia属性でprint用のCSSを別途用意すべきでしょう。

overflowというよりもブロック整形文脈がclearプロパティと同等の処理を行う

上の仕組みを考えると「overflow:hidden」がclearプロパティと同等の処理を行っているようですが、ブロック整形文脈がclearプロパティと同等の処理を行っていると表現すべきです。
なぜかというと、たまたまブロック整形文脈を作るプロパティである「overflow:hidden」を使っただけで、実は他にもブロック整形文脈を作るプロパティが存在します。そしてこれらのプロパティを使用してもclearプロパティと同等の処理を行うことができるのです。

overflow が visible 以外の場合い以外にブロック整形文脈を作るプロパティ

  1. floatの値がnone 以外の場合
  2. position が absolute または fixed である場合
  3. display: inline-block である場合
  4. display: table-cell である場合
  5. display: table-caption である場合
  6. display が flex または inline-flex である場合

最後のdisplay が flexの場合は単にclearの代わりというよりも、floatに代わるレイアウトの新しい手法になりますので別途説明します。
上で紹介したブロック整形文脈を作るプロパティはそれぞれ独特の動きをしたりするものです。そのためこれらをclearの代わりに使用すると指定した親要素が今度は変な挙動を始めます。そのためレイアウト的に通常フローと変わりない「overflow」プロパティが使われているのです。
このことを知っておけば、状況に応じて例えばフロートを複雑に入れ子状態にしたレイアウトでは、親要素にfloatを設定するとその子供要素のフロートを解除することができます。わざわざここに別途「overflow」プロパティを使う必要はないのです。
これらのことを理解して使いこなせるようになればきっとコーディングが楽しくなるはずです。

clearfixを使う方法

clearfix記述の例

clearfixはclearプロパティを使うことと全く同じことを行なっています。
clearプロパティはフロートしている要素と兄弟になる後続要素に設定する事でしたが、うまいことそのような候補になる要素が存在しない場合に使えません。
そのような場合にafter擬似要素を使用することでCSSから動的にフロートしている親要素の一番最後に子要素を作成し、その作成した子要素にclearプロパティを設定する手法です。この方法はそもそも存在しない要素を作成して、それを人には見えないようにした上で、ブロック要素の性格を持たせて、clearプロパティを設定することであまりに強引ですからできればあまり使いたくない手法でもあります。
また初心者には擬似要素の使い方は少し難易度が高くて(親要素に対して擬似要素を使うことなど)混乱の原因になる場合が多いです。
clearfixはCSSの使い方にすっかり慣れてから挑戦すれば良いと思います。それはだいたいは「overflow:hidden」で解決できるからです。
ちなみにBootstrapもclearfixを使用しています。

ブロック整形コンテキストの特性を利用したレイアウト

ブロック整形コンテキストの特性を利用したレイアウトの作成方法を紹介します。
まずは基本的な固定の2カラムレイアウトを作成します。
表示結果

左右のカラムを固定サイズにする場合は、左右のカラムにfloatプロパティを設定してその後、親要素に「overflow:hidden」を設定すれば出来上がります。
その際親要素の横幅は左右のカラムの幅を計算して算出した固定サイズになります。

固定レイアウトのコード例

固定レイアウトのコードサンプル
HTMLコード

cssコード

右カラム可変レイアウトのコード例

右カラム可変レイアウトサンプル
右のカラムのサイズをリキッドタイプにする場合は少し工夫が必要です。
この場合は右カラムのサイズは「auto」にして左カラムの横幅と同じサイズの左マージンを右カラムに設定すると実現できます。
右カラムにはfloatプロパティの設定はありませんので親要素は右カラムの高さを解釈して囲んでくれます。ただし、左カラムの方が高さが高い場合は正しく囲めません。そのため親要素には「overflow: hidden;」を入れておくと良いでしょう。

ブロック整形コンテキストの特性を利用した右カラム可変レイアウトのコード例

ブロック整形コンテキストの特性を利用した右カラム可変レイアウト
右カラム(親要素ではありません)に「overflow:hidden」を設定すると右カラムは自動でサイズがちょうど良いサイズに変わります。
上で紹介した方法と同様に、右カラムにはfloatプロパティの設定はありませんので親要素は右カラムの高さを解釈して囲んでくれます。ただし、左カラムの方が高さが高い場合は正しく囲めません。そのためこの場合も親要素には「overflow: hidden;」を入れておくと良いでしょう。

これはバグではなく、ちゃんと仕様書に記述されている通りの動きです。
ブロック整形コンテキストの仕様の下記の記述部分です。

ブロック整形コンテキストにおいて、各ボックスの左外辺は包含ブロックの左辺と接する(右から左に整形の場合、右辺が接する)。これは、浮動体の前であっても当てはまる(あるボックスの行ボックスが浮動体の分だけ縮むが)。ただしボックスが新しいブロック整形コンテキストを設置する場合を除く(その場合、ボックス自身が浮動体のためにより狭くなってもよい)。

上記引用文の中で「ただしボックスが新しいブロック整形コンテキストを設置する場合を除く(その場合、ボックス自身が浮動体のためにより狭くなってもよい)。」の記述です。これをわかりやすく表現すると、ブロック整形コンテキストとなるプロパティ(overflowなど)を設定するとその要素はフロートした要素を避けるように幅を短くしても良いということです。

CSSコード

floatとclearを使ったレイアウトはなかなか面倒なところがありますが、どの手法を使ってclearと同等の結果を得るにしてもクロスブラウザに対応していることが重要です。

最後に念のため、「overflow:hidden」がベストと言っているのではありませんので誤解のないようにしてください。ケースバイケースで使い分けるのがベストですが、入門者のアプローチとしては「overflow:hidden」から始める方が近道だということと、ブロック整形コンテキストの特性を知ってより高いコーディング力をつけて行くことです。