【11-4】marginの相殺(margin collapsing)
最終更新日:2017年11月13日 (初回投稿日:2015年11月21日)marginには相殺(margin collapsing)と呼ばれる独特のルールがあります。
相殺(そうさい)とは、差引きして帳消しにすること(そうさつ とも読みます)。この日本語訳はなかなか絶妙です。元の collapsing のほうは「崩壊・破綻」などという意味。
margin collapsing は、2つの接するマージンが結合して1つになること。
相殺されて1つになったマージンを collapsed margin と言います。
ブロックレベルの要素で、
マージンが上下に接していると、結合して1つのマージンに なるんです。
兄弟関係の要素で、上下に接するマージンは大きいほうだけがイキ(小さいほうはボツ)になったり、
親と子要素のマージンでは、子のマージンが親の外に突き出したり...なんて挙動もします。
そして、ある条件下ではマージンの相殺が起こらないとか...ちょっとややこしめですw。
ややこしいけど、このmarginの相殺はぜひ知っておくべき。CSSでの「?」がグッと減ります。
本日はこの marginの相殺を、サンプルで実験しながらじっくり見てみます。
marginの相殺が起きる場合のルール
ブロックレベルの要素で、margin と margin の間に他のものがなんにも無くて、
上下のmargin同士が直接触れ合っている場合に相殺が起きます。
兄弟要素なら、margin は一番外側なので、どうしても触れ合いますね。
親子関係なら、親要素に padding や border が無ければ、子の margin と直に触れ合います。
触れ合った margin 同士は1つの 相殺されたマージン(collapsed margin)になります。
どちらか幅の大きいマージンがイキ、小さい方は吸収されちゃいます。
同じ幅なら片方だけのマージン幅になります。
例えば、10px と 30px が触れ合ってたら 30px のマージンになる。足して40pxにはならない。
また、2em 同士だったら、2em のマージンになる。足して4em にはならないんです。
親子の要素の場合は、親要素に padding か border があれば、それが「壁」になって親の上下のマージンを子のマージンと触れ合わないようにしてくれるので、子のマージンとの相殺は起きないんです。これは覚えておこう。
(兄弟の場合は、どうしても触れ合っちゃいます。マージンは一番外側なので。)
相殺されるのは上下マージンだけ。
左右のマージンは相殺されないんだよね。これもぜひ覚えておきましょう。
ですので、私はこんなイメージを持っています。
内容(content)や padding、border、左右のmarginは、さわれば固い 固体のイメージ。
上下のマージンだけはバリアのような、幅はあるけど実体のないイメージ。
左右のマージンが相殺されないことを、ちょっと実験して確かめてみます。
HTMLはこちら。
<div id="sample1">
<span>span</span><span>span</span><span>span</span><span>span</span>
</div>
CSSはこちら。
div#sample1 {
border:solid 1px #ccc;
padding:0;}
div#sample1 span {
padding:0.5em;
background:#ffe982;
line-height:4em; /*インラインは上下のmarginが効かないので、line-height で調整します*/
margin:0 2em;} /*上下のmarginを0、左右を 2emにしています*/
横に並ぶインラインの要素の <span> を使ってみました。
左右のマージン2emずつがちゃんと足されて4emずつ間が空いています。これが相殺のないノーマルな状態ですね。
(先頭の<span>の左側の空きが 2em なのを基準に見比べてください。<span>どうしの間隔はその倍。わかりにくいけど)
インラインじゃなくブロックレベルの要素で実験したかったのですが、ブロックレベルを横に並べるための「display: inline-block」や「flort」を使うと、縦のマージンの相殺すら起こらないんです。(これは後半のmarginの相殺が起こらない場合で説明)
で、ここでは話がややこしくなるのでインラインの <span> を使いました。
しかし、もともとインラインの要素は上下のmarginが効かないという性質ですので、相殺のサンプルとして使うのはイミ無い。
とにかく 相殺は「上下マージンだけ」「ブロックレベル要素だけ」の話です。
では、ブロックレベルの要素の マージンの相殺 について、
サンプルを見ながら順に確認していきましょう。
上下に隣接する兄弟要素の marginの相殺
同じ親の中にいる「兄弟」の要素の場合、上下に隣接するマージンは相殺されます。
マージンの大きい方だけ採用されて、小さい方は吸収されます。
これは h4要素。上マージン1em、下マージン2em
これは p要素です。上マージン1em、下マージン2em
もうひとつ p要素。上マージン1em、下マージン2em
HTMLはこちら。
<div id="sample2">
<h4>これは h4要素です。上マージン1em、下マージン2em</h4>
<p>これは p要素です。上マージン1em、下マージン2em</p>
<p>もうひとつ p要素。上マージン1em、下マージン2em</p>
</div>
CSSはこちら。
div#sample2 {
border:solid 1px #ccc;
margin:0;
padding:0;}
div#sample2 h4, div#sample2 p {
padding:1em;
margin:1em 1em 2em;
background:#ffe982;
line-height:2em;}
div#sample2 h4 {font-size:100%;}
div#sample2 p {background:#cf6;}
サンプルの h4要素も p要素も、上のマージンは 1em、下のマージンは 2em です(7行目)。
お互いの間には、1em + 2em で 3em 空きそうなんですが、2em しか空いていません。
親要素のmarginと子要素のmarginの相殺
親要素に padding や border が無い場合に、
親要素の上のマージンと、最初の子の上のマージンが、相殺されます。
同じく、親要素の下のマージンと、最後の子の下マージンも、相殺されます。
これは h4要素です
これは p要素です。
HTMLはこちら。
<div id="sample3">
<section>
<h4>これは h4要素です</h4>
<p>これは p要素です。</p>
</section>
</div>
CSSはこちら。
div#sample3 {border:solid 1px #ccc;margin:0; padding:0;}
div#sample3 section {
margin:1em;
padding:0;}
div#sample3 h4, div#sample3 p {
padding:1em;
margin:1em 0 2em;
background:#ffe982;
line-height:2em;}
div#sample3 h4 {font-size:100%;}
div#sample3 p {background:#cf6;}
サンプルでは、親の<section>要素の上マージンは 1em です。
最初の子要素<h4>の上マージンは 1em なので、合計の 2em になるかと思ったら、相殺されて1em になります。
また、親の下マージンは 1em ですが、最後の子要素<p>の下マージンが 2em なので、こちらも相殺されて、大きい方の 2em になっています。子のマージンが親の外に突き出してるかんじ。
●親に padding や border があれば相殺されない
上のサンプルは、親に padding や border が無いので、親のマージンと子のマージンが直接くっついていたので相殺が起こりました。
下のサンプルは、親に border を指定した例です。親に padding や border があって、子のマージンと親のマージンが直接触れ合わなければ、相殺は起こりません。
これは h4要素です
これは p要素です。
CSSはこちら。(HTMLはさきほどと同じです)
div#sample31 {border:solid 1px #ccc; margin:0; padding:0;}
div#sample31 section {
margin:1em;
padding:0; /*親に padding を指定しても同じく相殺は起こりません*/
border:dotted 1px red;} /*親にボーダーを指定しています*/
div#sample31 h4, div#sample31 p {
padding:1em;
margin:1em 0 2em;
background:#ffe982;
line-height:2em;}
div#sample31 h4 {font-size:100%;}
div#sample31 p {background:#cf6;}
空のブロックは 自分自身の上下のmarginが相殺されます
高さも無い、中身も無い、border も padding も無いブロックレベルの要素は、自分自身の上下のマージンが相殺されます。
サンプルを見てみましょう。
こちらは、親の<section>要素の最初と最後の子要素として <hr>要素を入れています。
<hr>要素は、自分自身のmarginの相殺で、高さが3emの相殺されたマージンだけの表示になり、
さらにそれが、親のマージンや兄弟のマージンとも相殺を起こしています。
これは h4要素。上に hr要素があります
これは p要素。下に hr要素があります
HTMLはこちら。
<div id="sample4">
<section>
<hr>
<h4>これは h4要素。上に hr要素があります</h4>
<p>これは p要素。下に hr要素があります</p>
<hr>
</section>
</div>
CSSはこちら。
div#sample4 {border:solid 1px #ccc;margin:0; padding:0;}
div#sample4 section {
margin:1em;
padding:0;}
div#sample4 hr {
height:0;
border:none;
padding:0;
margin:3em 0 2em;}
div#sample4 h4, div#sample4 p {
padding:1em;
margin:1em 0 2em;
background:#ffe982;
line-height:2em;}
div#sample4 h4 {font-size:100%;}
div#sample4 p {background:#cf6;}
図解するとこうなってます。
1番上のマージンは、まず<hr>の上下マージンが相殺されて3emだけになり、それが兄弟の<h4>のマージンとも相殺し、親の<section>のマージンとも相殺して3emだけになってます。
真ん中のマージンは、兄弟どうしの<h4>と<p>のマージンの相殺で2emですね。
下のマージンも、<hr>自信の相殺 + 兄弟<p>と親の<section>のマージンとの相殺になってます。
ネガティブ・マージンの場合の相殺
ネガティブ・マージンがある場合も、マージンの相殺は起きます。
正のマージン(引っ込む)と、負のマージン(はみ出る)があるなら、引っ込む分とはみ出る分の差になります。これは普通の計算というか、素直にわかりやすいかも。
どっちも負のマージンなら、今までの正のマージンどうしと同じで、幅が大きい方がイキ。
サンプルを見てみましょう。
親である<ul>要素には、上マージン 2em、下マージン -1em を指定しています。
子の<li>要素のうち、最初の<li>には 上マージン -3em、下マージン 3em を、
最後の<li>には逆に 上マージン 3em、下マージン -3em を指定しています。
- List1
- List2
HTMLはこちら。
<div id="sample5">
<ul>
<li>List1</li>
<li>List2</li>
</ul>
</div>
CSSはこちら。
div#sample5 {border:solid 1px #ccc; font-weight:bold; padding:0; margin:2em 0 5em;}
div#sample5 ul {
padding:0;
margin:2em 0 -1em;}
div#sample5 ul li {
margin:-3em 1em 3em;
padding:1em;
list-style:none;
background:rgba(102, 153, 255, 0.5);
border-radius:5px;
color:#fff;
line-height:1em;}
div#sample5 ul li:last-child {margin:3em 1em -3em;}
marginの相殺が起こらない場合
ここまで marginの相殺のルールを確認してきましたが、marginの相殺が起こらない場合もあるんです。これが CSSで感じる「?」のモトかもしれません!
相殺しない場合について、サンプルを見ながら確認しておきましょう。
まず、親子の場合は、親に border や padding があれば相殺は起こらないんでしたね。親の border や padding が「壁」になって子のマージンと触れ合わないから。
コレ↑以外に、
要素に display, float, position, overflow などのプロパティを使っているとき、マージンの相殺が起こらない場合があります。
ここらへんを「兄弟」「兄弟も親子も」「親子」の場合で整理してみました。
(このほかにもあるかもしれないけど、今のところの思いつくかぎりのマージン相殺のまとめ...です)
display:inline-block な兄弟要素の場合(兄弟)
ブロックレベルの要素を横に並べる時に便利な display: inline-block ですが、
display: inline-block を指定した兄弟同士では、マージンの相殺が起こらなくなります。
サンプルを見てみましょう。
上下に接したp要素のマージンは相殺されると 1em のはずですが 2em になっていますね。
p要素
p要素
p要素
p要素
HTMLはこちら。
<div id="sample6">
<p>p要素</p><p>p要素</p><p>p要素</p><p>p要素</p>
</div>
CSSはこちら。
div#sample6 {border:solid 1px #ccc; width:14em;}
div#sample6 p {
display:inline-block;
text-align:center;
line-height:3em;
width:5em;
background:#ffe982;
margin:1em;}
display:flex の中のFlexアイテムな兄弟要素の場合(兄弟)
Flexboxを作る display: flex を要素に指定すると、中の子要素はぜんぶ「Flexアイテム」と呼ばれるモノになって、兄弟同士の「Flexアイテム」はマージンの相殺は起こらなくなります。
サンプルを見てみましょう。
Flexアイテム同士の上下のマージンは相殺されると 1em のはずが、2em になっていますね。
2つの Flexbox を縦に連ねていますが、親同士は上下のマージンが相殺されています。
Flexアイテム
Flexアイテム
Flexアイテム
Flexアイテム
Flexアイテム
Flexアイテム
Flexアイテム
Flexアイテム
HTMLはこちら。
<div id="sampleFlex">
<div id="fc1"> <!-- ←これをFlexboxに指定-->
<p>Flexアイテム</p><p>Flexアイテム</p><p>Flexアイテム</p><p>Flexアイテム</p>
</div>
<div id="fc2"> <!-- ←これもFlexboxに-->
<p>Flexアイテム</p><p>Flexアイテム</p><p>Flexアイテム</p><p>Flexアイテム</p>
</div>
</div>
CSSはこちら。
div#sampleFlex {border:solid 1px #ccc;}
div#fc1, div#fc2 {
display:flex;
flex-wrap: wrap;
background:#ffe982;
margin:1em;}
div#fc1 p, div#fc2 p {
flex:1 0 200px;
text-align:center;
line-height:3em;
background:#ffc;
margin:1em;}
float を指定した要素の場合(兄弟も親子も)
float が指定されている兄弟同士では、マージンの相殺は起こりません。
また、親要素に float が指定されている場合、親子のマージンの相殺は起こりません。
サンプルを見てみましょう。
float
float
float
float
- List
- List
HTMLはこちら。
<div id="sample7" class="clearfix">
<p>float</p><p>float</p><p>float</p><p>float</p>
<ul>
<li>List</li>
<li>List</li>
</ul>
</div>
CSSはこちら。
div#sample7 {border:solid 1px #ccc; width:14em;}
div#sample7 p, div#sample7 ul {
float:left;
text-align:center;
line-height:3em;
width:5em;
background:#ffe982;
margin:1em;}
div#sample7 ul { width:12em;}
div#sample7 ul li {margin:1em; background:#3cc; list-style:none;}
p要素・ul要素ともに float:left を指定しています。(3行目)
すると、縦に並んだ p要素どうし、p要素と ul要素の間のマージンは、相殺されずに 2em ですね。
また、ul要素は、子要素 li とのマージンの相殺が起こらず、最初のli要素の上マージンも、最後のli要素の下マージンも 1em ちゃんと効いています。
float:left の指定だけ抜いてみたらこうなります。上のと見比べると一目瞭然ですね。
float
float
float
float
- List
- List
余談だけど、flort している要素と連接していると
これは、マージンの相殺というよりも、floatの性質の実験ですが、ついでに見てみましょう。
先ほどのサンプルでは、p要素も ul要素も float させましたが、
ここでは、ul要素の float をやめて、ul要素の上マージンを 5em にしてみます。
すると、すぐ上のp要素(floatしてる)との間が 5em 空くのではなく、親のdiv要素の border-top からのマージンになるんです。
float を指定された要素(ここではp)は、まさに「浮いちゃってる」ので、他の兄弟要素とのマージン相殺の対象にならない(相殺も何もありゃしない)ということがわかります。
float
float
- List
- List
HTMLはこちら。
<div id="sample8" class="clearfix">
<p>float</p><p>float</p>
<ul>
<li>List</li>
<li>List</li>
</ul>
</div>
CSSはこちら。
div#sample8 {border:solid 1px #ccc; width:14em;}
div#sample8 p, div#sample8 ul {
float:left;
text-align:center;
line-height:3em;
width:5em;
background:#ffe982;
margin:1em;}
div#sample8 ul {
width:12em;
float:none; /*ul要素だけ floatなしに*/
margin-top:5em; /*ul要素の上マージンだけ 5em にしてみた*/
background:#fcc;}
div#sample8 ul li {margin:1em; background:#3cc; list-style:none;}
clear を指定した要素の場合(兄弟も親子も)
上のサンプルで ul要素に「float:none」と指定した所を「clear:left」にしてみます。
clear を使うとマージンの相殺は起こらなくなります。
ul要素の上マージンは 1em に戻しています。
p要素と ul要素の間は 2em になって、兄弟同士のマージンの相殺が起こっていませんね。
clear を指定した ul要素は、子要素liとのマージン相殺も起こっていません。
float
float
- List
- List
HTMLはこちら。(すぐ上のサンプルとまったく同じです)
<div id="sample9" class="clearfix">
<p>float</p><p>float</p>
<ul>
<li>List</li>
<li>List</li>
</ul>
</div>
CSSはこちら。
div#sample9 {border:solid 1px #ccc; width:14em;}
div#sample9 p, div#sample9 ul {
float:left;
text-align:center;
line-height:3em;
width:5em;
background:#ffe982;
margin:1em;}
div#sample9 ul {
width:12em;
clear:left; /*ul要素だけ clearの指定をします*/
background:#fcc;}
div#sample9 ul li {margin:1em; background:#3cc; list-style:none;}
親が overflow:visible 以外の overflow の指定のとき(親子)
親が overflow: hidden, auto, scroll のとき、ってことはoverflow: visible 以外の overflow の指定のとき 親子のマージンの相殺は起きません。
visible は overflowのデフォルト値です。デフォ以外のoverflowを指定した時ってことですね。
下のサンプルは、親の ul要素に「overflow: hidden」を指定しています。
(わかりやすいように ul要素にも背景色を付けています)
- List1
- List2
HTMLはこちら。
<div id="sample10">
<ul>
<li>List1</li>
<li>List2</li>
</ul>
</div>
CSSはこちら。
div#sample10 {border:solid 1px #ccc; font-weight:bold; padding:0; margin:0;}
div#sample10 ul {
overflow:hidden;
padding:0;
margin:1em;
background:rgba(102, 153, 255, 0.5);}
div#sample10 ul li {
margin:1em;
padding:1em;
list-style:none;
background:rgba(102, 153, 255, 0.5);
border-radius:5px;
color:#fff;
line-height:1em;}
overflowの指定が無ければ、このように親(ul)子(li)の相殺が起きます。
- List1
- List2
overflow: auto や overflow: scroll で、スクロールバーが出る状況でも、こんなかんじで、
スクロールバーの内側に子のマージン、外側に親のマージンができ、相殺は起きません。
- List1
- List2
親が position: absolute または fixed のとき(親子)
親が position: absolute または fixed のとき、親子のマージンの相殺は起きません。
親が position: absolute の場合
サンプルは、ul要素に position: absolute を指定し、その親の div に対して絶対位置にしました。
すると、ul要素と子要素 li のマージンの相殺は起きていないのがわかりますね。
- List1
- List2
HTMLはこちら。
<div id="sample11">
<ul>
<li>List1</li>
<li>List2</li>
</ul>
</div>
CSSはこちら。
div#sample11 {border:solid 1px #ccc; font-weight:bold; padding:0; margin:0;
position:relative; /*position:relative を指定して、absolute の親にします*/
height:11em;}
/*中身が絶対位置指定のモノだけなので高さ指定してます。高さが無いとぺったんこだから*/
div#sample11 ul {
position:absolute; /*divに対しての絶対位置指定*/
top:0; /*上辺からの距離 = 0にすることで、自分自身のマージンがそのまま効きます*/
left:0; /*左辺からの距離*/
right:0; /*right:0 が無いと、li要素の幅は自分の内容(content)の幅に依存します*/
padding:0;
margin:1em;
background:rgba(102, 153, 255, 0.5);}
div#sample11 ul li {
margin:1em;
padding:1em;
list-style:none;
background:rgba(102, 153, 255, 0.5);
border-radius:5px;
color:#fff;
line-height:1em;}
positionプロパティを使わなければ、このようになります。
- List1
- List2
親が position: fixed の場合
position: fixed は、ブラウザの表示領域(viewport)に対しての相対位置に固定されます。
画面の左下を見てください。(スマホの人は画面下。うっとおしくてゴメンね)
今回ず〜っと固定表示されていたコレが、position: fixed でのサンプルです。
サンプルでは、上のサンプルと同じく div要素の中に ul, li要素を入れています。
ul要素を「position: fixed」に指定すると、親の div要素でなくブラウザの表示領域に対しての相対位置になり、スクロールしても固定(fixed)されます。
ul要素と 子の li要素のマージンの相殺は起きずに、それぞれ 1em のマージンが効いていますね。
もし相殺していたら、li要素の上下のマージンが無くなって、ulにくっついているはずですから。
以下、サンプルです。(といってもul要素はココ↓じゃなく画面下に固定されていますけどね)
- *1番最後の 親が position: fixed の場合 のサンプルです。
HTMLはこちら。
<div id="sample11_1">
*これはdiv要素。中身の ul要素は、ブラウザウィンドウの右下に固定表示されています。
<ul>
<li>*1番最後の <a href="#pt2-6fix">親が position: fixed の場合</a> のサンプルです。</li>
</ul>
</div>
CSSはこちら。
div#sample11_1 {border:solid 1px #ccc; padding:0; margin:0; font-weight:bold;
position:relative;} /*親が position:relative でも無関係。ビューポートに対しての fixed になります*/
div#sample11_1 ul {
position:fixed; /*viewportに対しての固定位置(fixed)になります*/
bottom:0; /*下辺からの距離 = 0にすることで、自分自身のマージンがそのまま効きます*/
left:0; /*左辺からの距離*/
padding:0;
margin:1em;
background:rgba(102, 153, 255, 0.5);}
div#sample11_1 ul li {
margin:1em;
padding:0.3em;
list-style:none;
background:rgba(102, 153, 255, 0.5);
border-radius:5px;
color:#fff;
line-height:1em;}
div#sample11_1 ul li a { color:#fff; text-decoration:underline;}
次回予告
いかがでしたか?
今回はサンプルてんこ盛りで、読むのも大変だったでしょう?
相殺には、ひと言でバシッと言えない「ややこしさ」があるので長くなりました。
しかも float、clear、overflow や position などのプロパティも出てきたしw これらはいずれ詳しく取り上げる予定です。
マージンの相殺を知っていると、表示が意図したものにならないときも、慌てなくて済みます。
相殺を見込んで シンプルな CSSを書けるようになれば、とっても便利です。
さて次回は、padding について。
padding は、ややこしく無くて、ショートハンドの話だけ。気楽です♪
- 関連記事
-
- 【15-3】background関連のプロパティ(3/4 background-size)
- 【15-2】background関連のプロパティ(2/4 background-position)
- 【15-1】background関連のプロパティ(1/4)
- 【14】borderとよく似た outlineはレイアウトを崩さない
- 【13-2】border-imageと border-imageのショートハンド
- 【13-1】borderと borderのショートハンド
- 【12】paddingと paddingのショートハンド
- 【11-4】marginの相殺(margin collapsing)
- 【11-3】はみ出て便利♪ ネガティブ・マージン(Negative Margin)
- 【11-2】marginの auto という値の「?」を解決しておこう
- 【11-1】marginと marginのショートハンド
- 【10】ボックスモデル(margin, padding, border を使いこなそう)
- 【9】HTML要素の インラインレベル・ブロックレベル について
- 【8】CSSの「色」と「透明度」の指定
- 【7-2】CSS3の新しい単位 vw, vh, vmin, vimax について
初心者にも使いやすい(と思う)レンタルサーバー
「レンタルサーバーはどこがいい?」とご質問をよくいただきますが、自分でも使っていてオススメなのは
スターサーバー
(ミニバードがスターサーバになりました)。管理画面がわかりやすくていい感じす。
仕事で使ってるロリポップ!もわかりやすい管理画面で、初めてでもすんなり使えます。
両方とも、なんといっても料金が安いです。
ちょっと料金は高いけど、高スペックでコスパが良く、信頼性も高いサーバといえば、やはりさくらのレンタルサーバと、エックスサーバー
。この2つは老舗でユーザーも多いので、質問する場がたくさんあり、初心者の方でもイケるだろうと思います。
スポンサーリンク