3.3 ポジショニングと重ね合わせ

通常フロー、浮動化に加え、CSSにはもう一つのポジショニング(位置決め)スキーム、絶対位置決めが用意されています。ボックスは通常フローからは切り離され、機能にとって相応しい場所に配置することができます(*54)。絶対位置決めで配置したボックスが重なる時の順序は、レイヤ配置によって入れ替えることも可能です。

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

3.3.1 positionプロパティと位置決め

ボックスの位置決めスキームに関係するもうひとつの重要なプロパティがpositionです。

positionの値が'static'以外(*56)の場合、ボックスの外辺(*57)のオフセット位置はtopbottomrightleftプロパティによって示します。値は<長さ>、包含ブロック(CBL)3.1.6.6の幅/高さに対する<パーセント>(*58)もしくは'auto'で(*59)、初期値は'auto'です。継承は行いません。

(図)

top, right, bottom, leftは、絶対位置決めの場合は、ボックスのそれぞれの外辺が包含ブロックの対応する辺からどれだけ反対方向に(内側に)離れた位置に置かれるかを表す

positionプロパティ値を'absolute'もしくは'fixed'とする位置決めスキームを絶対位置決め(absolute positioning)と呼びます。ボックスは外辺がそれぞれのCBLからtopleftなどのオフセットの位置になるよう配置され、その内部に新たなブロック整形コンテクストを形成します(*60)

絶対位置決めで配置されたボックスは、他のボックスの配置に影響せず、マージンの折り畳みは生じません。ボックス同士が重なり合う場合、後述のレイヤ配置によって前面、背面の順序を決めることができます。

なお、ここでは説明のために、positionプロパティ値が'static'以外の値('relative'、'absolute'、'fixed')をとるものを布置ボックス(仕様書ではpositioned box)と呼ぶことにします。

positionプロパティ値に関連して、いろいろな呼び名や役割が交錯するので、一覧表にしておきましょう。

position:staticrelativeabsolutefixed
配置別名(通常配置)相対配置絶対配置(*61)固定配置
本章のボックスタイプ名-布置ボックス
位置決めスキーム通常フロー絶対位置決め
包含ブロック直近ブロックボックスの内辺直近布置ボックスのパディング辺表示域
top、leftなど適用しない通常配置からのオフセット包含ブロックからのオフセット
ブロック整形コンテクスト形成しない新たに形成
浮動化可能不可
*54
ボックスが前後の要素の並びから切り離されると、多くの場合、文脈が失われたり大きく変化したりします。絶対位置決めに際しては、その要素にとって文書内での順序が重要な意味を持っていないか、スタイルシートを無効にした場合と伝わる意味が違ってこないかを、慎重に考える必要があります。
*55
ルート要素に指定した場合、"User agents may treat position as 'static' on the root element."とされており、一般にブラウザはルート要素を通常配置として扱います(body要素を絶対配置することはできます)。テーブルセルや行にも指定可能ですが、挙動はブラウザによって異なります。CSS2-98では、セクション17.5の注で"Table cells may be relatively and absolutely positioned, but this is not recommended"とされていました。セルや行のpositionにこれらの値が指定されると、ボックスはテーブルのセル・行ではなくなり、テーブル全体のレイアウトが崩れることがあります。また、CSS2-98では生成内容(generated content)は対象外とされていましたが、この制約はCSS2.1ではなくなりました(Opera8、Safari1.3が対応しています)。
*56
CSS2-98では、position:staticの場合は「leftとtopは適用しない」となっていましたが、CSS2.1では「top、right、bottom、leftいずれも適用しない」に改められました。
*57
CSS2-98では内辺(content edge)のオフセットとなっていますが、正誤表及びCSS2.1で外辺(margin edge)に訂正されています。
*58
パーセントの場合、ボックスモデルのパディングやマージンは上下であっても包含ブロックの幅に対して算出されますが、位置決めのtop、bottomは包含ブロックの高さを基準にします。なおCSS2-98では、高さが明示されない包含ブロックに対する%は'auto'として扱うという規定がありましたが、CSS2.1では削除されています。これについては、3.4.1.3の注も参照してください。
*59
'auto'の場合は、一般的には、通常フローでの位置になるようにtop、leftが算出されます(CSS2正誤表では'static-position'という値が追加されましたが、CSS2.1では削除されています)。詳しくは3.4.1を参照してください。
*60
つまり、その内部では新たな通常フローが始まります。
*61
CSS2仕様書の表現では、"absolute positioning"という用語はpositionが'absolute'および'fixed'の両方を合わせた位置決めスキーム、あるいは「配置モデル」を指す(CSS21:9.6)ものとして使われており、position:absoluteだけを表す特別な用語は示されていません。しかし、説明に際して不便なので、本章では便宜的にposition:absoluteである配置を「絶対配置」と呼び、'absolute'および'fixed'の配置モデルを「絶対位置決め」とします。なお、単に"positioned"というときはpositionが'static'以外の要素を指す(CSS21:9.3.2)となっていて紛らわしいですが、本章ではこれを「布置ボックス」と呼ぶことにします。

3.3.1.1 positionプロパティと浮動化

一つのボックスに対して絶対位置決めと浮動化を同時に指定することはできません。positonプロパティの値が'absolute'もしくは'fixed'である場合は、floatプロパティの算出値は'none'となり、絶対位置決めが優先されます†CSS21:9.7

position:relativeとfloatは同時に指定可能です。この場合については、3.3.4.3を参照してください。

3.3.2 絶対配置

positionプロパティの値が'absolute'の場合、CBLは 最も近い祖先の“布置ボックス”によって形成されます(*62)。この祖先ボックスが:

*62
CSS2のレイアウトモデルの前身にあたる"Positioning HTML Elements with Cascading Style Sheets"草案では、包含ブロックという概念はなく、絶対位置決めボックスの配置基準としてpositioning contextというものがありました。CSS2の草案に至って、これが発展する形で、通常フローのボックスも含む包含ブロックが登場してきます。
*63
インラインボックス(IB)が複数の行ボックスにまたがる場合、包含ブロックの「左上」よりも「右下」が左に来ることがあり得ます。このときは、仕様書がいうように、幅が負の値になってしまいます。ただしブラウザの実装は、包含ブロックがIBによって形成されるとき、布置ボックスのwidthを%で指定すると、IBの包含ブロックとなるブロックボックス(B)を基準にして幅を算出しているようです(Bの内辺を基準にするものと、パディング辺を基準にするものがあります)。

[例3.24]

<body>
 <h1>絶対位置決め</h1>
 <div class="section">
  <img class="sec_img" src="sec_icon.png" alt="SECTION" />
  <p>通常フロー、浮動化に加え、CSSにはもう一つの位置決めスキーム…</p>
  ...
 </div>
 ...
</body>

このXHTMLに対して、次のスタイル規則を適用してみます。

[例3.25]

div.section {position:relative; border:1px solid gray; padding:1em}
img.sec_img {position:absolute; top:0; right:0}

画像のボックスにとってのCBLは、最も近い祖先布置ボックス、すなわち親要素であるdiv.sectionのパディング辺になります。したがってこの場合は、div要素の枠の内側に沿って、右上に画像が配置されます。

(図)

div.sectionのパディング辺が包含ブロックになるので、その右上端に画像が配置される

ここでもし、div.sectionが通常配置のボックス(positionが初期値'static')であるとすると、画像の祖先には布置ボックスが存在しなくなります。この場合のCBLは、ルールにより文書のIniCBとなるため、画像はウインドウの右上に表示されることになります(*64)

(図)

絶対配置の対象が初期包含ブロックになると、画像は文書全体の右上、すなわちブラウザウインドウの右上に配置される

IniCBは、「キャンバスの原点に固定された、表示域のサイズの長方形」です3.1.6.6。したがって文書が最初に表示された時点では表示域と一致しており、画像はウインドウ右上に表示されます。しかし、ウインドウをスクロールすると、表示域はキャンバスの原点から離れ、IniCBの下方向に動いていく(*65)ので、画像も隠れてしまいます。

*64
オフセットがtop:0ではなくbottom:0で与えられる場合は、文書全体の右下ではなく、ウインドウの右下に表示されます。ただし、CSS2-98セクション9.1.2の「初期包含ブロックの高さは、height:autoなら文書内容をおさめられるように拡大する」という(CSS2.1では削除された)定義を採用するブラウザ(Opera8など)では、画像は文書右下に表示されてしまいます。
*65
スクロールボタンを下に移動させるという操作は、表示域を下に移動させることに他なりません。実際は、ウインドウの中でレンダリングされた文書がスクロールボタンとは逆方向に動くかたちなので、表示域が固定されていて、キャンバスが移動するというほうが感覚的にはしっくり来るかも知れません。いずれにしても、初期包含ブロックはキャンバスの原点に結びつけられているので、スクロールによって表示域とは離れていきます。

3.3.3 固定配置

positionプロパティの値が'fixed'の場合、CBLは表示域によって形成される長方形エリア(ページ出力メディアの場合はページボックス)となります(*66)。例3.24のXHTMLに対して、次のスタイル規則を適用してみましょう。

[例3.26]

img.sec_img {position:fixed; top:0; right:0}

画像は表示域に対するオフセットに配置されるので、やはりウインドウの右上に置かれます。

これは一見するとposition:absoluteでIniCBCBLとする図3.29の場合と同じですが、固定配置のCBLはスクロールしても表示域から離れません。したがってposition:fixedであるボックスは、常にウインドウ内の同じ位置に固定されることになります(*67)

(図)

左はposition:fixed、右はposition:absoluteの場合。fixedの場合は包含ブロックが表示領域と一致するので、スクロールしても画像はウインドウの同じ位置に止まる。absoluteの場合は、スクロールに合わせて画像もウインドウの外に動いてしまう

印刷出力の場合は、ページボックスに対して固定されるので、この例ならば、全てのページの右上に画像が印刷されます。

*66
固定配置は、初期のpositioning仕様の草案には存在しておらず、CSS2の最初の草案で、絶対位置決めの包含ブロックを変えたバリエーションという位置づけで登場しました。IE6は固定配置に対応していませんが、これはIE4がこの草案の前に登場していたことに関係するかも知れません。IE7では、position:fixedもサポートされる模様です。
*67
固定配置に対応しないブラウザの場合は、このプロパティは無視されて通常の配置となってしまいます。固定できるかどうかはともかく初期状態で位置を移動させることが重要ならば、{position:fixed !important; position:absolute} という具合に、固定配置と絶対配置の宣言を異なる重要度でカスケードすることで、'fixed'が理解できるブラウザには固定配置として、そうでなければ絶対配置としてボックスをレイアウトさせることができます。そのほか、.section > .sec_img {position:fixed} といった具合に、IE6の理解できないセレクタに固定配置の宣言を与える方法も使われます(IE7は子要素セレクタもサポートするということです)。

3.3.4 相対配置

あるボックスAのpositionプロパティの値が'relative'の場合、Aは通常のフローとして配置された上で、topbottomrightleftプロパティで与えられるオフセット値の分だけ移動されます(*68)。この移動は、Aに続くボックスBの配置に影響を与えません(*69)。BはあたかもAが元の位置にあるかのように、通常フローに従って配置されます。移動されたAは、通常フローに基づく元のサイズをそのまま保持します(*70)

(図)

*68
この配置モデルは絶対位置決めに対して相対位置決め(relative positioning)とも呼ばれますが、広い意味での位置決めスキームとしては通常フローの一種で、包含ブロックも通常フローと同じく直近の祖先ブロック要素の内辺です。
*69
CSS2.1では、position:relativeをテーブルの行やセルに適用した場合の効果は"undefined"とされています。
*70
複数の行ボックスにまたがって分割されているインラインボックスに適用した場合は、分割された形のままで移動します。

leftプロパティ値が与えられた場合、ボックスAはその分右に移動します(左にオフセットをとる)。rightプロパティ値の場合は逆に左に移動します。

Aは元のサイズを保持するので、これらのプロパティは常に left = -right の関係となります。leftrightともに'auto'の場合(初期値)は、算出値はいずれも0となり、ボックスは移動しません。もしleftrightに異なる値が与えられた場合、テキスト方向が左から右の場合はrightの値が、逆ならばleftの値が無視されます。

[例3.27]

<body>
 <h1>相対配置</h1>
 <div class="section">
  <img id="fig1" src="fig1.png" alt="相対配置による移動..." />
  <p>
   ここで<em id="A" class="box">relativeとなるボックス</em>Aと
   <em id="B" class="box">その後継ボックス</em>Bの位置関係を…
  </p>
 </div>
 ...
</body>

上のXHTMLに対して、次のスタイル規則を適用してみます。

[例3.28]

.box {border:dotted 1px gray; font-style:normal; background:#ffdcdc}
#A {position:relative; top:50px; left:90px}

ボックスAは相対配置で下方向に50px、右方向に90px移動し、Aがもとあった場所は空白のまま残っています。

(図)

相対ポジショニングによるテキストボックスの移動。元の場所には空白が残る

3.3.4.1 絶対配置の包含ブロックとしてのposition:relative

position:relativeのボックスは、position:absoluteであるボックスのCBLとなり得ることを利用すると、画像に文字を重ねて説明するようなレイアウトが可能になります。スタイル規則を次のように変更してみましょう。

[例3.29]

.box {border:dotted 1px gray; font-style:normal; background:#ffdcdc}
div.section {position:relative}              /* 親要素をrelativeに */
#A {position:absolute; top:50px; left:90px}  /* Aはabsoluteに変更 */
#B {position:absolute; top:0; left:150px}    /* Bもabsoluteで配置 */

div.sectionがボックスA、BのCBLとなるため、これらのボックスはdiv.sectionのパディング辺からのオフセットに配置され、結果として画像上にラベルが加わる形になります。

(図)

包含ブロックにposition:relativeを使うと、うまい具合に画像の上にテキストを重ねて表示できる

これを応用すれば、画像を分割してテーブルレイアウトを利用するといった旧来型の手法を用いることなく、不規則な形の画像の任意の位置にキャプションを与えることが可能になります。

ここで、div.sectionのpositionプロパティを'absolute'としても、ボックスA、Bは同じように画像上に配置されます。しかし、div.sectionは通常フローに存在しないものとなり、続くボックスが上に移動してくるため、テキストどうしが重なって意図とは異なるものになってしまいます。

(図)

包含ブロックがposition:absoluteだと、後続のボックスが上に移動して重なってしまう

相対位置決めでは、div.sectionは通常のフローとして配置されるので、後続のボックスが重なってしまうことはありません(*71)

*71
IE6では、position:relativeのボックスAを包含ブロックとしてボックスBをposition:absoluteで配置する場合、Bの直後のボックスに左マージンが指定されていると、Bのleftプロパティにそのマージンが加算されてしまいます。このとき、Aに幅、高さ、あるいはマージン、枠、パディングのいずれかが指定されていると、この奇妙な現象は生じません。