CSS おれおれ Advent Calendar 2012 – 05日目

横幅いっぱいに広げようとしてwidth:100%を指定したら横スクロールバーが出ちゃった、という経験ありませんか? その原因と仕組み、回避策についてお話しします。

わりとFAQな感じ。これは是非覚えておいてもらいたいです。というか知っておいてください。

はみ出した例

position:absoluteないしposition:fixedを使った際にこうなっちゃう事が多いと思います。

上部から出てきたメッセージ欄、横にはみ出てますね。

でも指定はちゃんとwidth:100%です。「幅いっぱい」を指定しているのに、どうしてはみ出てしまったんでしょうか。

「幅」が意味するところ

実はCSSの仕様が定めるところの「幅」というのが、我々が視覚的に認知している「幅」と異なっているのが原因です。

ボックスモデル

CSSではこんなものを定めています。

CSS Box Model

いわゆるボックスモデルというやつです。

普通、人が領域を見て「幅」と認識するのは、枠線 (Border) までです。

Border box

ところがこのボックスモデルでは、幅と高さというのは内容 (Content) に対しての指定になります。

Content box

この認識の差が問題になります。

幅はwidthだけじゃなかった

つまりContentの部分がきっちり横いっぱい100%に広がって、さらにPadding分の幅も加わっていたのです。さらに指定があればBorder分、Margin分も広がります。

だからpadding:8pxとした場合、左右8pxずつ、合わせて16px分余計な幅が出来てしまうわけです。

回避策

はみ出させないようにするには、二つのやり方があります。

入れ子にする

width:100%の指定とpaddingの指定が共存しているのが問題でした。

そこでwidth:100%で横いっぱいに広げた要素の中で、幅を指定しないでpaddingだけ指定した子要素を設置します。(この場合はmarginに置き換えた方が直感的かもしれません。)

内側の要素は幅を指定していないので、自動的にwidth:autoになります。この場合はMarginまで含めた幅がいっぱいに広がるようになります。

外側にwidth:autoを指定する、というのはうまくいきません。position:absoluteを指定している状態では、autoは最大ではなく最小幅になるためです。

このやり方は古いIEでも正確に表現できるのですが、余分な要素が増えてしまうのが難点です。

box-sizingを使う

先ほどのボックスモデルの説明でサイズの取り方が二通りあるという話をしました。box-sizingというCSSプロパティを用いると、その二通りを指定する事ができます。

box-sizing:border-boxを指定する事で、width(とheight)で指定するサイズがBorderまで含めたものを指す事になりました。

Border box(再掲)

このやり方が一番綺麗です。

IEは8以降が対応しています。7は対応していません。6は、対応はしていないのですが、最初からborder-boxの挙動になっています。(不具合でした。) 末尾の参考リンクのところにIE 7で動かせるようにしているページを紹介しています。

あとFirefoxではベンダープレフィックスが必要です。(Ver.1.0の頃からずっと……。)

あとあと、Android等ではちゃんと動くらしいです。ってばっちゃがゆってた。

ボックスモデルの考え方

これは仕様策定の様子を知る訳ではなくて、ただの当て推量ですが……。

何故このような「不自然」な仕様になっているかというと、おそらく見方を変えるとこの方が実は「自然」だからだと思います。

例えばContentが画像であった場合を考えると、こちらの方がしっくりくるのがわかります。

画像の幅と高さは如何や

画像に枠線を付けたとしたら、その枠線は画像の外側に付くべきですよね。内側に付くと画像にかぶってしまいます。

このとき幅と高さは、何を指定するべきでしょうか。100×100pxの画像に5pxの枠線を付けたとして、imgのスタイルwidthとheightに与えるべき数値は何でしょうか。

枠を付けてみました。

当然width:100px, height:100pxですよね。そして表示は100pxに両端5pxずつの枠が付いて、合わせて110px。これならしっくり来ませんか。

Paddingは余白じゃない

また、辞書で”padding”を引いてみると、余白ではなく「詰め物」という語で表されています。(”margin”だと欄外の余白ですね。)

つまり先ほどの例でいくと、Paddingは画像と枠線の間に詰めた隙間という事になります。これもまた、幅と高さに含めるのは不自然ですね。

という風に解釈しているのですが、いかがでしょうか。

まあ分かり辛いけどね

要素によって初期値を変えるとかでも良かったんじゃないかなーとか思わないでもないです。はい。

参考