Web Memo

Web作成に少し慣れてくると間違いに気付いたりよく理解していなかったことなどが分かってきます。これからの Web 技術なども含めて気付いたこと、役に立ちそうなことをメモしたものです。

CSS の margin が悩ましい、難しい、効かない?

CSS でレイアウトをしていくと margin が指定どおりとれなくて、ブラウザのバグだろうと片付けたりしていませんか? 下記の例は、親ブロック内に子ブロックがある一般的なもので、 ボーダー、パディングがない親子ブロック間のマージンに関するものです。

【CSS 】
#parent {
  background-color: #69f;
  height: 120px;
}
#child {
  background-color: #fcf;
  margin-top: 40px;
}

【HTML】
<div id="parent">
  <p id="child">子ブロック margin-top: 40px;</p>
</div>
【 表示例 】

背景にある格子の最小目盛サイズは、5px (ピクセル)です。色合いにより 10px、50px、100px になっています。

【 図 A 】 Before親子ブロック間マージンサンプル図(Before)
【 図 B 】 After親子ブロック間マージンサンプル図(After)

親のdiv 要素ブロックと子の p 要素ブロック間にマージンをとりたいので p 要素に margin-top をいれて図 B のように表示したいのですが、図 A のように親のdiv 要素ブロックがマージン分下がるように表示 します。(IE7以前は、図 B のように表示されます)

これを解決するには、次の3通りの方法があります。

  1. 親ブロックにボーダー border をいれる。
  2. 親ブロックにパディング padding-top:40px;をいれる。
  3. 親ブロックに表示方法を制御する、overflow プロパティを使用する。
    overflow:visible 以外の auto、scroll、hidden が使えます。ただし、auto、scrollは、スクロールバーが表示される場合があるので overflow:hidden が適しています。

親ブロックにはボーダー、パディングがないので子ブロックと接している状態です。親ブロックにボーダーを入れられない場合は、パディングpadding-top:40px;にするか、 overflow:hidden;を使うことで解決します。

【方法1】
#parent {
  background-color: #69f;
  height: 120px;
  padding-top: 40px;
}
#child {
  background-color: #fcf;
  margin-top: 40px;
}
【方法2】
#parent {
  background-color: #69f;
  height: 120px;
  overflow: hidden;
}
#child {
  background-color: #fcf;
  margin-top: 40px;
}

以上の対処法を理解するためには、次の包含ブロック(containing block)や、マージンの特性を理解する必要があります。

Top↑

包含ブロックとは

CSS でのレイアウトの基本は、「ボックスの概念」で簡単に説明していますが、すべての要素(ブロックレベル、インライン)は、矩形の領域を生成し margin、border、padding により配置、表示されます。インライン要素(a、em、span など)のマージンは水平だけで、上下は効きませんが同じように扱われます。

さらに、包含ブロック(containing block)という概念を理解しなければなりません。W3C仕様書の邦訳版では「包含ブロック」と訳されていますが、「内容領域」とか「コンテナ」とも呼ばれ、ボックスのサイズや位置を計算するために必要になります。

【 div 要素で形成された p 要素の包含ブロック 】
包含ブロック説明図

子要素である p 要素の配置場所や大きさは、どのように決まるのか?

図のように親要素である div のパディング内側に配置されます。そして p 要素の幅も、親要素である div のパディング内側に広がります。 p 要素の配置場所や大きさは、p 要素自身ではなくて、上の図で外側点線部分の div 要素内容領域が決めていることになります。この部分を div 要素が作りだした、「p 要素の包含ブロック」と言います。

包含ブロックは、子孫要素を配置することができる領域であり、要素の配置場所や大きさは、祖先の要素が作り出した包含ブロックによって決まります。包含ブロックは、パディングがある場合はパディングの内側、無い場合はボーダーの内側と一致します。

今回のサンプルでは、ボーダーがなかったので親要素の div ブロックに padding-top 指定することで、div 要素に上下の幅ができます。上図の包含ブロックの考え方から、 親ブロックの padding-top: P 、子ブロックの margin-top: M とすると、P+M= 40px になるように両方を指定しても良いことになります。 ただし、親ブロックの上辺を特定するために、padding-top: P は、" 0 " を越える値が必要です。

p 要素の margin-top が効かなかったのは以上の理由ですが、div 要素のボーダー上辺と、p 要素のボーダー上辺が一致した場合、子ブロックの p 要素で指定したマージンが、 親要素を通り越して div 要素のマージンのように働きます。これを、「マージンの相殺」といいます。(マージンの相殺は後述)

表示制御プロパティ overflow

display:none とした要素は、ボックスを全く生成しませんが、overflow:hidden とすると、ボックスを生成した上、ボックス領域からあふれた部分を不可視にします。そのためボックス領域はレイアウト上の空白として残り、子要素である p 要素は、 外形を認識できるようになります。このテクニックは、親要素による float の解除にも使われます。

包含ブロックの定義

包含ブロックとは、祖先要素のうち、もっとも近い祖先の要素が作りだしたブロックボックスの内容領域を指します。ボックスの位置やサイズは、その包含ブロックと呼ばれる矩形領域を基準に計算されます。例えばwidthプロパティの%値は、包含ブロックの横幅を参照します。 レイアウトに関係する margin、padding、float、positionプロパティを使いこなすには、包含ブロックの定義をよく理解しておく必要があります。

一般に、ある要素が生成したボックスは、その子孫ボックスの包含ブロックとして機能します。このことを、その要素が子孫のために包含ブロックを形成する(establish)と表現します。 通常、「ボックスの包含ブロック」という場合、「そのボックスが含まれている祖先要素の内容領域としての包含ブロック」を意味し、その「ボックス自身が形成した包含ブロック」という意味ではないのでご注意下さい。 そのボックス自身も子孫要素のために包含ブロックを作っているので紛らわしいですね。表現するのが難しい!

また、絶対配置の場合は少し変わってきます。包含ブロックの定義を、表にして簡単にまとめると次のようになります。


【包含ブロックの定義】
要 素 包含ブロック
ルート要素 画面表示域(初期包含ブロック
通常フロー要素 直近の祖先であるブロックレベル要素、インラインブロック要素
または、テーブルセル要素のボックスの内辺
浮動ボックス float
position:relative
position:absolute 直近の祖先で position プロパティが static 以外の
absolute、relative、fixed である要素のパディング辺
該当する要素がなければ、初期包含ブロック(画面左上原点)
position:fixed 画面表示域

内辺とは、テキストや画像が描画される表示済み内容領域をかこむ内容辺です。
パディング辺とは、ボックスのパディングをかこむ辺です。絶対配置の場合は、場所が特定されるのでパディング辺になります。パディングがないならパディング辺と内容辺は同じになります。

参考

Top↑

マージンの相殺

「通常フローの上下に隣接するブロック間同士」や、冒頭の例のように、「ボーダーやパディングが間にない親子関係にあるボックス間」に限り、隣接するマージン同士が結合してひとつのマージンになります。 これを「マージンの相殺」といいます。左右のマージンの相殺はありません。

隣接するマージン同士の大きい方がマージンになる

二つのボックス A、B が上下に並んだ例です。背景の格子の最小目盛サイズは、5pxで、色合いにより 10px、50px、100px になっています。

【HTML】
<div class="a">Box-A</div>
<div class="b">Box-B</div>
【CSS 】
.a {
  background-color: #fcf;
  margin-bottom: 50px;
  height: 50px;
}
.b {
  background-color: #69f;
  margin-top : 30px;
  height: 50px;
}
Box-A
margin-bottom: 50px;
Box-B
margin-top: 30px;

マージンは、50px + 30px とはならず、大きいほうの 50px になります。どちらも負のマージンの場合も、負の値が大きい方になります。

正負が混在している場合は、足し合わせた値がマージンとなる

【CSS 】
.a {
  background-color: #fcf;
  margin-bottom: 50px;
  height: 50px;
}
.b {
  background-color: #69f;
  margin-top : -20px;
  height: 50px;
}
Box-A
margin-bottom: 50px;
Box-B
margin-top: -20px;

マージンは、50px +(-20px)= 30px となります。負の値のほうが大きい場合は、負の値になります。従って、相殺はないということになります。

親子ブロックのボーダー上辺が一致する場合、親の上マージンと子の上マージンは相殺される

親と子(または、入れ子)の関係の場合、親と子のボーダー上辺が一致している場合に限り、相殺が起こります。冒頭の例は、子ブロックの上マージンが親ブロックを通り越して相殺される例です。 親ブロックにパディングが 1px でも入っていれば親ブロックの外辺を認識でき計算できるので相殺は起こらず、子ブロックで指定した上マージンがとれます。

パディング、ボーダーとも入れられない場合は、overflow:hidden を親要素に指定しても相殺を回避できます。

相殺が起きない場合

相殺が起きない場合をまとめると下記のようになります。

Memo

  • 水平方向(左右)のマージン
  • パディングやボーダーによって分離された親子関係にあるブロック間のマージン
  • フロート配置されたボックスと、他のあらゆるボックス間のマージン
  • 絶対配置(position の値が absolutefixed )のボックスと、他のあらゆるボックス間のマージン
  • 親要素に、overflow:visible; 以外のauto、scroll または、hidden が指定された親子ブロック間のマージン
  • flex コンテナのマージンとその内容のマージンとの間も相殺されることはない

フロート配置と隣接するボックスのマージン

フロートされたボックスは、通常フローから外れ文字通り浮いた状態で右寄せ、または左寄せで配置されます。従って、後続の通常フローボックスとの マージンの取り方が変わってきます。


【HTML】
<div id="wrapper">
  <div id="header">Header</div>
  <div id="main">Main</div>
  <div id="side">Side</div>
  <div id="footer">Footer</div>
</div>
<!-- Main、Side部はフロート配置 -->

Header と Footer が通常フロー、 Main と Side をフロート配置で並べ、全体を Wrapper で囲む2カラムレイアウトの例で、フロート配置されたボックスと隣接ボックス間のマージンがどのようになるか確認してみます。

フロートを解除しない場合

【 図 A 】 Footer部 clear: nonefloat マージン説明 HTML

フロート配置の Main と Side は、高さを失っている状態なので Footer は、Header のすぐ下に潜り込むように配置されます。(図A)  従って、フロートボックスの下マージンは効きません。Footer の上マージンは、先行するフロートボックスを通り越して Header との間で計算されます。フロートボックスの高さが固定の場合に限り、その高さを含めて通常フロー同士の Header と Footer 間のマージンを指定するようになります。


【 図 B 】 Footer部 margin-top:120pxfloat マージン説明 HTML

(図B)は、フロートボックス Main に下マージン、margin-bottom:30px; と、Footer に、margin-top:120px; を指定した例です。Header で指定された下マージン10px は相殺されて、Header と Footer 間のマージンは、 フロートボックスの高さ分を含めて 120px になるように配置されます。Main の下マージン30px は、Footer ボックスに対しては効きません。フロートに続く Footer 内の行ボックステキストは、フロートボックス右側の余白に回り込みます。

Memo

  • フロートされたボックスは通常フローから外れる。
  • フロート直後の clear しないボックスの上マージンは、先行するフロートボックスの下マージン指定では機能しない。
  • フロート配置されたボックスと、隣接するボックス間のマージンの相殺はない。

フロートを解除した場合

【 図 C 】 Footer部 clear: bothfloat マージン説明 HTML

Footer を clear プロパティでフロートを解除すると、Footer のボーダー上辺が先行するフロートの外下辺よりも下に来るように上マージンが自動調整され、先行フロートボックスに接するように配置されます。(図C)  先行フロートボックスとの上マージンは、先行フロートボックス側の下マージン指定が機能するようになり margin-bottom:10px が指定できるようになります。また、行ボックス内のテキストは回り込まなくなります。(図D)


【 図 D 】 Main部 margin-bottom:10pxfloat マージン説明 HTML

フロート解除しなかった(図B)のように、Footer に上マージン margin-top を指定しても、通常フロー同士の Header と Footer 間のマージンになります。この例では、フロートボックスの高さが固定ならばその高さを含めたマージン 120px 以上の値を指定すればよいことを確認できます。


Memo

  • フロート直後の clear したボックスの上マージンは、先行するフロートボックスの下マージンで指定する。

参考

行ボックスを使ってフロート配置の挙動とマージンの関係など記述してあります。重複するところがありますがご参考まで。

Top↑

インラインボックスの表示とマージン

ブロックレベル要素に対し、文中の一部分だけ意味づけや書式設定を行うものをインライン要素と言います。よく使うもので a、br、code、em、img、span、strong などがあります。 インライン要素の表示とマージン、パディングをブロックレベル要素と比較してみます。

1. 通常インライン配置 margin: 0; padding: 0;

下記の(匿名インライン)+(親インライン+子インライン)+(匿名インライン)という構成の、行ブロック内インライン要素のサンプルでマージン、パディングを確認してみます。

匿名インラインボックスはスタイル指定ができないので「匿名インラインボックス」と呼ばれ、親ブロックから継承可能なプロパティだけ継承します。

HTML
<p>ここは匿名インライン
<span id="outer">親ボックス始まり
  <span id="inner">ここは子ボックス</span>
  親ボックス終わり
</span>
  ここでインライン終わり(ここも匿名インライン)
</p>
CSS
p { 
  font-size: 15px;
  line-height: 1.5;
  border: 1px solid gray;
}
span { 
  line-height: 100%;
  border: 1px solid red;
}
#outer { 
  color: red;
  background-color: #ffc; 
}
#inner { 
  color: blue;
  background-color: #cff; 
}
【 表示例 】
通常インライン配置説明図

2. 親ボックスにマージン、パディング margin: 1em; padding: 0.5em;

親ボックスにマージン、パディングをいれた例です。マージンは始まりと終わりの水平方向だけで、パディングは上下左右に効きます。

#outer {
  margin: 1em;
  padding: 0.5em;
  color: red;
  background-color: #ffc; 
}
インライン配置内の親ボックスにマージン、パディング例

3. 子ボックスにマージン、パディング margin: 1em; padding: 1em;

子ボックスも同じようにマージンは始まりと終わりの水平方向だけで、パディングは上下左右に効きます。パディングが拡がったときは、左から右の順に周囲と重なって表示されます。行の変わり目の分割面は、マージン、ボーダー、パディングの視覚効果は現れません。 IE7 以前のブラウザは、親ボックスのパディングも拡がります。

#inner {
  margin: 1em;
  padding: 1em;
  color: blue;
  background-color: #cff; 
}
インライン配置内の子ボックスにマージン、パディング例

4. 子ボックスをフロート float:right; width:5em; margin:0.5em

子ボックスを右フロート配置するとブロック要素と同じように、通常フローから外れ、インラインボックスの下に移動し、後続のインラインボックスは空いている左に回り込みます。インライン要素は、フロートするとブロック要素に変身してマージンは上下左右に効きます。

#inner {
  float: right;
  width: 5em;
  margin: 0.5em;
  color: blue;
  background-color: #cff; 
}
インライン配置内の子ボックスをフロートした例

5. 子ボックスのフロート解除 clear: right

インライン配置内の子ボックスのフロートを解除した例

フロートを解除する clear プロパティはブロック要素に限定の規格なのでインライン要素には機能しません。それでも br 要素だけは効くようです。

br 以外のインライン要素でも disply: block 指定すると clear プロパティが効くようになります。インライン要素はその内容に他のインライン要素を持つことができますが、 ブロックレベル要素を含むことはできないと規定されています。それでも display プロパティを使用すると、ブロック要素として表示されます。

<p>ここは匿名インライン
  <span id="outer">親ボックス始まり
    <span id="inner">ここは子ボックス</span>
    親ボックス終わり
  </span>
  <br style="clear: right;">ここでインライン終わり(ここも匿名インライン)
</p>

インライン要素の相対配置、絶対配置

position: relative、position: absolute の場合もブロック要素と同じように機能します。ここでは割愛しますが興味のある方は試してみて下さい。

付記

インライン要素とブロック要素の比較

ブロック要素はすべて可能ですが、インライン要素をまとめると下記のようになります。

Memo

  • margin 左右(水平方向)だけで始めの文字左側、終わりの文字右側に存在
  • padding 上下左右に存在
  • width 指定不可
  • height 指定不可
  • border、background 指定できる
  • 行の折り返し点は、すべて視覚効果がない
  • float、position 配置は可能

Top↑

outline プロパティでレイアウトの確認

border と同じ書式でブロックボックスの輪郭を表示するプロパティ outline で、すべてのボックスの輪郭を表示するとレイアウト上のマージン、パディングやマージンの相殺を確認チェックしやすくなります。

昔のOpera ブラウザに、これに似た機能がありました。最近はどのブラウザも開発者用ツール(F12 キー)として高度になり個別にボックスの周辺をチェックできますが、CSS に次の1行追加するだけで全体のレイアウトを一目瞭然で確認できます。

【CSS 】
* { outline: 1px solid red; }

border でも同じように表示できますが、自身のマージン領域を確保するのでレイアウトに影響します。 outline は、外形の外側に表示しますがレイアウトに影響しません。

【 表示例 】
outlineプロパティでレイアウトの輪郭によるmargin、paddingチェック例

Chrome の拡張機能 「Pesticide」でも同じことができます。画面右上にある虫のアイコンPesticideアイコンをクリックするとタグ別に色分け表示され、再クリックまたは再表示で消えます。

Top↑