3.2 通常フローと浮動化

要素が生成したボックスは、その大きさやタイプ、文書ツリー上の他の要素との関係、そして位置決めスキームによって配置が決められます。機能ブロックの配置にとって重要な役割を果たす位置決めスキームは、(1)通常フロー、(2)浮動化、(3)絶対位置決めの3つがあります†CSS21:9.3が、ここではまず通常フローによるブロックのレイアウトと、水平配置を可能にする浮動化の詳細を説明します。

書籍原稿からXHTMLを生成して公開しています。実装状況に関する注は、2005年末〜2006年初にかけて調べたもので、ブラウザのバージョンアップなどにより変化することがあります。

3.2.2 浮動化

通常フローではブロックボックスは常に縦方向に配置されますが、ブロックのレイアウトとしては、横方向に機能を並べる方が関係を把握しやすかったり使いやすい場合も少なくありません。

浮動化(float)は、ボックスを通常フローで配置した上で、右もしくは左に移動するものです。浮動化されたボックス(浮動ボックス)の右または左に余白がある場合、インラインボックスはその余白に回り込んで配置されます。包含ブロック(CBL)3.1.6.6は通常フローの場合と同じです。

浮動ボックスは、新しいブロック整形コンテクストを形成し、その内部では新たなコンテクストによる通常フローにしたがってボックスが配置されます(*38)

*38
浮動ボックスはその内部で新たなブロック整形コンテクストを形成しますが、それ自身は(通常フローからは外れるけれども)親のブロック整形コンテクスト内にあるものとして扱います。浮動化では、同じコンテクストに属する浮動ボックスと通常フローのボックスとの間で、回りこみなどが生じます。

3.2.2.1 float、clearプロパティ

ボックスがどちら側に移動するか(あるいはしないか)は、floatプロパティで指定します(*39)。float:leftであればボックスは左に移動し、インラインボックスはその右に回りこむことができます。float:rightであればその逆です。初期値は'none'で、継承されません。

回り込みの解除はclearプロパティで行います(*40)。clear:leftを指定すると、その要素の左側に浮動ボックスが配置されないようになり、右側への回り込みが解除されます。clear:rightはその逆方向の回り込みを、clear:bothはいずれの方向の浮動化に対しても回り込みを解除します。初期値は'none'で、継承されません。

なおclearは、このプロパティが設定された要素内部での浮動化、および異なるブロック整形コンテクストの浮動化に対しては影響しません。

*39
floatプロパティは、positionが'absolute'もしくは'fixed'である要素以外すべてに指定できます。CSS2-98では生成内容(generated content)も対象外とされていましたが、この制約はCSS2.1ではなくなりました(Opera8、Safari1.3が対応しています)。なお、ルート要素にfloatを指定した場合、"User agents may treat float as 'none' on the root element."とされています。回り込ませる対象もないので、指定したところで意味はないわけですが。
*40
clearプロパティは、CSS1では全ての要素に適用できましたが、CSS2ではブロックレベル要素に限定されています。

3.2.2.2 浮動化の整形規則

浮動ボックスは、その外辺がCBLもしくは他の浮動ボックスの外辺に接するまで左(右への浮動化なら右)に移動します。行ボックスがある場合は、浮動ボックスの上外辺は行ボックスの上辺に合わされます。先行する他の浮動ボックスがあって余白が不足する場合は、浮動ボックスは下に移動して配置されます。

浮動ボックスは通常のフローから外れるため、その前後のブロックボックスは浮動ボックスが存在しないかのように垂直配置されます。一方、浮動ボックスと同じ位置にある行ボックスは、その幅だけ短くなり、回り込みが生じます。

浮動化されたボックスAと同じ行ボックスで、Aより前にあったインラインボックスは、Aが左への浮動化ならAの右側に移動し、回り込む最初の行ボックスの内容として配置されます。

(図)

上は通常フロー、下は画像を浮動化した状態。浮動化したボックスは、通常フローのときに配置される行ボックスの左(右)に移動する。画像にはマージンを与えており、青の点線は外辺を示すために加えたもの。なお、IE6、Firefox1.5はこの仕様通りではなく、浮動ボックスを次の行ボックスに送って揃えてしまう

3.2.3 浮動化と周囲のボックス

浮動化によって機能ブロックをレイアウトするためには、浮動ボックスと周囲のボックスとの関係がどうなるかを理解しておくことが重要です。このセクションでは、浮動化についての詳細なルールに加え、浮動化とマージンなどの関係、周囲のボックスとの重なりについて確認します。

3.2.3.1 浮動化の整形規則の詳細

前項に述べた浮動化の配置は、細かく記述すると次の9つのルール†CSS21:9.5.1によって決定されます(*41)

  1. 水平配置の限界:左浮動ボックスの左外辺はCBLの左辺より左に出てはいけない。右浮動化の場合も左右を読み替えて同様(以下同じ)。
  2. 同方向の重なり禁止:ボックスAが左浮動化の場合、先行する左浮動ボックスBがあれば、Aの左外辺をBの右外辺より右にするか、もしくはAの上外辺をBの下外辺より下にしなければならない。
  3. 反対方向の重なり禁止:ボックスAが左浮動化の場合、その右に位置する右浮動ボックスCがあれば、Aの右外辺はCの左外辺より右に出てはいけない。
  4. 垂直配置の上限:浮動ボックスの上外辺は、CBLの上辺より上に出てはいけない。浮動ボックスが折り畳みの行われる2つのマージンの間にある(そのボックスを浮動化することで、前後のボックスが隣接してマージンが折り畳まれる)ときは、浮動ボックス以外を含まない匿名の親ブロックがフローに存在するものとして配置する。
  5. ボックス順序の保持:浮動ボックスの上外辺は、文書中それより前に記述された要素が生成するブロックもしくは浮動ボックスの上外辺より上に出てはいけない。
  6. 漸次レンダリング:浮動ボックスの上外辺は、文書中それより前に記述された要素が生成するボックスを含む行ボックスの上辺より上に出てはいけない(整形済の行を再描画しなくてよいようにする)。
  7. 溢れの禁止:左浮動化Aの左に他の左浮動化Bがあるとき、Aの右外辺はCBLの右辺より右に出てはいけない。
  8. 上詰めの原則:浮動ボックスは可能な限り上に配置する。
  9. 端寄せの原則:左浮動化は可能な限り左に、右浮動化は可能な限り右に配置する。ただし8.の上詰め配置が優先される。

浮動化配置の制約となるボックスは、同じブロック整形コンテクストに属する要素が生成するボックスのみ(*42)で、絶対ポジションなど異なる整形コンテクストのボックスは関係しません。

さらに、浮動ボックス自身にclearプロパティが適用された場合は、次の10番目のルールが加わります。

  • clear:leftが宣言された浮動ボックスの上外辺は、先行する全ての左浮動ボックスの下辺よりも下でなければならない(right、bothの場合も同様)。
*41
ルールの名前は、CSS3ボックスモデルモジュールの2002年10月草案で与えられている名称を参考にしたものです。CSS3とCSS2.1では、浮動化のルールの順序や構成が違っていますが、基本的には同じです。
*42
行ボックス内のテキストは、ブロック整形コンテクストではなくインライン整形コンテクストに属するので、ルール6は仕様書の「these rules refer only to other elements in the same block formatting context as the float」という記述と矛盾するように見えます。これは、インライン整形コンテクストがブロックボックスごとに形成されるということの延長で、インラインボックスはそのブロック整形コンテクスト内にあると考えるのでしょう。CSS2.1の検討課題163で"Inlines don't participate in the block formatting context."として挙げられていた疑問に対して、CSS WGの議長であるBert Bosは"Yes, they do"(いや、属する)とシンプルに答えています。3.2.1.2の注33も参照してください。

ルール1〜3および7は、複数の浮動ボックスがある場合、その横幅の合計がCBLの幅より小さければ横並びにすることができるが、そうでなければ最後の浮動ボックスは下に移動するということを意味しています。

(図)

左はA,B,Cの幅の合計が包含ブロックに収まっているが、右は合計が包含ブロックの幅を超えるため、Aが下に移動する

ルール4〜6は、浮動ボックス配置の上限を規定しています。浮動ボックスは、CBLより上に出ることはできず、さらに先行するボックスがある時はそれよりも上にできません。

4.の後半は2004年2月のCSS2.1第一次勧告候補で加わったもので、3.1.4.2で取り上げた空白ボックスのマージンと関連する規定です。次のケースの浮動ボックスの扱いを考えてみましょう。

[例3.15]

.sec p {margin:1em auto}
.sec img {float:left; margin:10px}
...

<div class="sec">
 <p>仕様書は、次のようなステップの処理モデルを示して…</p>
 <img src="css.png" alt="..." />
 <p>ルールの適用対象を決める「アドレシング」にはいくつか…</p>
 ...
</div>

このような要素のツリーの場合、div.secの生成するブロックボックスの中にブロックボックスとインラインボックスを混在させないため、img要素を含む匿名ブロックボックスが生成されます3.1.6.2。通常のフローだけでなく、このimg要素が浮動化しても同様に考え、浮動ボックスのCBLとして扱う(配置の上限とする)ことを示したわけです。

「浮動ボックス以外を含まない匿名の親ブロックがフローに存在するものとして」ということですから、上のXHTMLは次のように書くのと同じことになるはずです。

[例3.16]

<div class="sec">
 <p>仕様書は、次のようなステップの処理モデルを示して…</p>
 <div id="anon"><img src="css.png" alt="..." /></div>
 <p>ルールの適用対象を決める「アドレシング」にはいくつか…</p>
 ...
</div>

img要素が浮動化すると、div#anonの配置は内容を持たない場合と同様になり(*43)、前後のp要素と合わせてマージンが折り畳まれます☞3.1.4.2。このとき、「ボックスの枠上辺は、ゼロでない枠を持っている場合と同じ位置になる」と規定されていますから、#anonと2番目のp要素の上辺が一致することになります。従って、浮動化されたimg要素の上外辺は、2番目のp要素の上辺に接することになるわけです(下図左)。

(図)

CSS2.1の仕様通りなら、左のように2つのp要素ブロックの間のマージンは折り畳まれ、浮動ボックスの上外辺は2番目のp要素ブロックの上辺に接する。浮動ボックスを明示的にブロック要素で囲んだ場合、Opera8など一部ブラウザでは右のようにマージンを折り畳まず、2つのp要素ブロックの中間に浮動ボックスが置かれる

もし#anonが枠や上下パディングを持っていれば、#anonの上下マージンは折り畳まれませんから、上図右のように浮動ボックスは2つのp要素ボックスの間に置かれます。一部のブラウザは、#anonが枠、パディングを持たなくても折り畳みを行わず、右のようなレイアウトを行うようです。

*43
ボックスの高さの計算には、浮動ボックスや絶対位置決めボックスを含まないので、この場合はコンテント領域の高さが0となり、内容を持たないときと同じく、ボックスの上下マージンが隣接します。

3.2.3.2 マージン、枠、パディングと浮動ボックス

(オンライン版では、このセクションは省略します)

3.2.3.3 浮動ボックスと通常ボックスの重なり

浮動ボックスと通常フローのボックスが重なる場合もあります。ブロックボックスは浮動ボックスがないものとして配置されるので当然重なってきますし、インラインボックスもマージンが負の値をとったりすると重なることがあります。

このとき、ブロックボックスは浮動ボックスの背面に、インラインボックスは前面に描かれます(*45)

[例3.21]

#foo img {float:left; margin:10px;}
#foo span {margin-left:-2em; background:white}
#bar {border:#0b0 medium solid}
...
<div id="foo">
 <img src="css.png" style="margin:10px"/>
 <span>インラインボックスがマイナスのマージンを持つ場合…</span>
</div>
<div id="bar">ブロックボックスが不動ボックスと重なる場合は…</div>

#foo内のインラインボックスは負のマージンを持つため、浮動ボックスに重なります。#barは、背景および枠と浮動ボックスの関係が分かるように、目立つ枠線を加えています。

(図)

浮動ボックスと、ブロックボックス、インラインボックスの関係。浮動ボックス(画像)のマージンは透明なので、その部分だけブロックボックスの枠と背景が見えている

3.2.1.3で説明したように、ブロック整形コンテクストとインライン整形コンテクストは別の「レイヤ」で描画されますが、浮動ボックスはこの両者のレイヤの間に挿入される形になるのです。この詳細は、3.3.5.3で改めて取り上げます。

*45
CSS2-98では、「インラインボックスは浮動ボックスの前面に、ブロックボックスの背景、枠は背面、内容は前面に配置する」とされていましたが、ブロックボックス内のテキスト内容は(匿名を含めて)インラインボックスを形成するので、これは「インラインは前面、ブロックは背面」と実質的には同等で、CSS2.1でこの記述に改められました。

3.2.3.4 浮動ボックスとリスト

(オンライン版では、このセクションは省略します)

3.2.4 浮動化とクリア

浮動化をうまく制御するには、clearプロパティの働きをきちんと把握しておくことが不可欠です。

3.2.4.1 clearとボックスの高さ

大きなボックスを浮動化させたとき、インラインボックスが回りこんだ結果、親ボックスAの高さよりも浮動ボックスの方が背が高くなることがあります。通常フローのブロックボックスは、浮動ボックスがないものとして配置されるので、続くブロックボックスBが上に移動して、その内容が浮動ボックスの横に回り込みます。

(図)

浮動ボックスの包含ブロックAに続く、同じフローのブロックボックスBが上に移動して、テキストが回りこむ

ここで要素Bにclear:leftを指定すると、Bの左に浮動ボックスを置くことができなくなり、この回り込みは解除されます。

(図)

Bにclear:leftを指定すると、左側に浮動ボックスを置くことが禁止され、回りこみが解除される

このとき、親ボックスAは浮動ボックスを含む高さになっているのではなく、そのインラインボックスをおさめられるだけの高さしかありません(*48)。浮動ボックスは、Aからはみ出す形になります。

(図)

ボックスAの高さは、浮動ボックスを包むように伸びるのではなく、通常フローの内容分だけしかない。これはAに背景を与えてみると明らか

これは、通常のレイアウトではあまり問題になりませんが、親ボックスの枠と背景で浮動ボックスも囲もうと考えている時は、思惑とは違う結果を招くことがあります。

*48
ボックスの高さの計算には、浮動ボックスや絶対位置決めの子孫ボックスを含まないことになっています。ただし、ボックスAが浮動化やoverflowプロパティによってブロック整形コンテクストのルートとなっているときは、「子孫浮動ボックスがはみ出す場合は親ボックスの高さを延長する」という規定があります。3.4.1.5を参照してください。

3.2.4.2 clearとmargin

clearプロパティで回り込みを解除した場合、その要素のmargin-topで浮動ボックスのはみ出し分を埋めることができない時は、「クリアランス」という空白を付け加えて要素を下に移動させると考えます(*49)。マージンが十分である時は、クリアランスは生じません。

前の例を使って(分かりやすくするために画像のマージンを0として)、クリアランスを確認してみましょう。この場合、溢れが2em分の高さになるとすると、ボックスBのmargin-topとクリアランスは次のように扱われます(*50)

  • margin-top:1emの場合、溢れ分より少ないので、1emのクリアランスが加えられる。Bの枠は、浮動ボックス(画像)の下辺と接する。
  • margin-top:3emの場合、溢れ分の2emを使っても1emのお釣りが来るので、Bの枠は浮動ボックスの下辺より1em分下に置かれる。

(図)

clearプロパティを設定したボックスの上マージンが、浮動ボックスの溢れ分よりも小さいときはクリアランスが加えられる。マージンに余裕があれば、浮動ボックスとの間に余白を置く。左はBのmargin-topが1em、右は3emの場合。この図では、これまでの例と異なり、画像にはマージンを設定していない

ただし実際には、浮動ボックスの下にBに指定されたmargin-topをまるごと取ってしまうブラウザも少なくありません。Bのmargin-topを利用せず、浮動ボックスに必要なmargin-bottomを与えれば、浮動ボックスとBとの間隔を直接指定することができます。

*49
CSS2-98では、要素のmargin-topが必要分増やされるという定義になっていました。
*50
クリアランスは、clearを与えられているボックスBのマージンの折り畳みを行って、仮の配置をした上で処理するとされています。