position: absolute; の指定で要素が上下左右中央配置になる理由

position: absolute; の指定である要素を上下左右中央配置にするという記事について、仕様書内の該当箇所を挙げてその動作根拠について補足してみようと思います。

人様の人気エントリーに乗っかる感じで恐縮ですが、「CSSで block 要素を上下左右中央寄せにする、イマドキの方法。 : バシャログ。」 という記事が話題になっていたので、なんでその指定で上下中央配置になるのか補足してみます。

詳しい話は下記のリンク先をご覧ください。

何でこういうことを書くかというとですね、例えば CSS でこういう指定をするとこう表示されるっていう話に関しては、仕様書を基準に話して欲しいのです。

「なんかよくわからないけどこういう風に書いたらこういう表示になった。よかったね」 で終わらせるのは個人の自由ですが、仕様書を基になぜそうなるのかの根拠を知るとより一層理解が深まると思いますので。

なので余計なお世話なんですが、下記に小難しく書きます。

解説のためのサンプルソース

例えば、下記のような HTML があったとして、

<div class="test">
 <h1>Absolutely positioned element</h1>
 <p>text...</p>
</div>

これに対して下記のようなスタイルを適用します。

.test {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
  width: 300px;
  height: 300px;
}

この場合、div.test は、現状のモダンブラウザにおいて、ブラウザのウィンドウに対して上下左右中央に配置 (つまり画面のど真ん中ですね) されると思います。もちろん、ウィンドウサイズがこの要素より小さければ別ですけどね。

まず基本的なことですが、position: absolute; された要素の配置に関して、その基準となるのは、「最も近い位置指定された祖先」 または、それが存在しない場合は 「初期包含ブロック」 です。

「位置指定された祖先」 というのは、要するに、position: static; 以外が指定された親要素と言うことになります。つまり、div.test の親となる要素に、position: relative; などが指定されていれば、その親要素が基準になりますが、そのような要素がない場合は、ルート要素が基準になりますので、通常はブラウザの画面表示領域を基準に配置されます。

で、リンク先の記事を読んだ方の中には、position: absolute;top / bottom / left / right 各プロパティに対して 「0」 が指定されているので上下左右中央配置になると思っている方もいるかもしれませんが、実はこれら値が 「0」 であることにはそれ程意味はありません (後述します)。

それよりも、これら指定に、margin: auto; が組み合わされていることが重要なポイントです。

仕様書の関連箇所

まず、前述した絶対配置される要素がどこを基準にして配置されるかについては、下記に記述があります。

ここは基本的なことなので置いておいて、次に、上下左右中央配置になる理由というか、関連する仕様が書かれている箇所ですが、これは、「幅とマージンの計算」 に関する決まりをまとめた下記のセクションにそれぞれ書かれています。

上記は、非置換要素 (div 要素など) の場合ですが、置換要素 (img 要素など) の場合については、それぞれ次のセクション (10.3.8 / 10.6.5) に。どちらも、絶対配置された要素について書かれている部分です。

まずは幅の方について該当箇所を抜き出すと下記になりますが、

The constraint that determines the used values for these elements is:

'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = width of containing block

If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0. Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.

If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values, unless this would make them negative, in which case when direction of the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value. If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that value.

10.3.7 Absolutely positioned, non-replaced elements から引用

難しそうなことを細かく書いてありますが、重要なのは、

'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = 包含ブロックの幅

となる点と、

If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values,

「その 3つ (その前の文脈から left / width / right の 3プロパティ) の値が auto じゃなくて、margin-leftmargin-right の両方が auto の時、2つのマージンが等しい値になるように計算」

と書かれている部分なります。つまり、上の条件を満たすように左右マージンを計算すると、左右中央に配置されます値という話。

これは、絶対配置でない (通常フロー)、非置換要素に関しても実はルールが同じなので、margin: 0 auto; とかして、ブロックレベル要素を左右中央に配置できることになります (古い IE がそれで中央配置にならないのは、仕様書と異なる解釈をしているためです) というか、よくやる記述ですよね。

同じように、縦方向に関しても見てみましょう。絶対配置された非置換要素に関する記述を見てみると、

For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:

'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom' = height of containing block

If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.

If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto', solve the equation for that value. If the values are over-constrained, ignore the value for 'bottom' and solve for that value.

10.6.4 Absolutely positioned, non-replaced elements から引用

のように書かれています。幅の計算の時とほぼ同じですね。

'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom' = 包含ブロックの高さ

となる点と、

If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra constraint that the two margins get equal values.

「その 3つ (その前の文脈から top / height / bottom の 3プロパティ) の値が auto じゃなくて、margin-topmargin-bottom の両方が auto の時、2つのマージンが等しい値になるように計算」

ということで、これも条件を満たそうとすると、上下中央に配置されると言うことになります。

では、絶対配置されていない、通常フローの非置換要素に関しても、margin-topmargin-bottom の両方を auto にすれば、幅と同じように、上下中央に配置されるの? と思うかもしれませんが、通常フローの要素に関する配置規則を書いた 「10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements」 セクションに、

If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.

margin-top もしくは margin-bottomauto の場合、マージンの使用値は 0 となる」 とありますので、縦方向に関しては、絶対配置された要素以外について、margin-top / margin-bottom の指定だけで上下中央に配置することはできません。

補足

先に top / bottom / left / right 各プロパティに対して 「0」 が指定されていなくてもいいよと書きましたが、そこについて補足。

まず、top / bottom / left / right 各プロパティの初期値は auto です。

これら値が auto だったときの margin: auto; の解釈については、同じセクション (10.3.7 / 10.6.4) にそれぞれ下記のように書かれています。

  1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
  2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position. Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
  3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
  4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
  5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
  6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'

「(2) left / right の値が autowidthauto 以外の場合、direction プロパティによる。つまり、ltr なら左寄せ、rtl なら右寄せ。」

といった感じ。つまり左右中央にはならない。

高さの方も、

  1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7, set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
  2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
  3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then the height is based on the content per 10.6.7, set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
  4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', then set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
  5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', then 'auto' values for 'margin-top' and 'margin-bottom' are set to 0 and solve for 'height'
  6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', then set 'auto' values for 'margin-top' and 'margin-bottom' to 0 and solve for 'bottom'

「(2) top / bottom の値が autoheightauto 以外の場合、top を基準として、margin-top / margin-bottom の値を 0 とする」

ということで、これも上下中央ではなく、上寄せになります。

つまり、上下左右中央になる計算を適用する条件としては、top / bottom / left / right 各プロパティに対して auto 以外の値を指定しなければならないと言うことですね。

逆に言えば、上に書いた 「全部足して包括ブロックの幅 / 高さ」 になる計算に合うのであれば、「0」 でなく、下記のように指定しても、同じく上下左右中央に配置されることになります。

.test {
  position: absolute;
  top: 10px;
  left: 10px;
  right: 10px;
  bottom: 10px;
  margin: auto;
  width: 300px;
  height: 300px;
}

わかりにくいので、「0」 にしておけばよいと思いますが、理屈上はそういうことになります。

まとめ

ということで、

ある要素を上下左右中央配置にしたい場合、絶対配置 (position: absolute;) にした上で、top / bottom / left / right 各プロパティに対して 「auto」 以外を設定し、margin: auto; をあわせて指定すると CSS の仕様を正しく解釈するブラウザ上では実現できるよ。

ということです。

なお、古くから使われているというマイナス マージン (負の値を与えた margin プロパティ) を使用する方法は、ブラウザの画面サイズが中央に位置した要素ブロックより小さいときに画面から要素がはみ出して見えなくなるので使わない方がいいと思います。

あと、これは余談ですが、同記事内で書かれている

写真をセンター寄せしつつ小さいサイズで切り抜きしたい場合など便利かもしれません。

に関しては、clip プロパティ使った方がいいんじゃね? と思います。

記事をここまで御覧頂きありがとうございます。
この記事が気に入ったらサポートしてみませんか?