1. 1. margin collapsing 条件
  2. 2. 阻止margin collapsing的方法
    1. 2.1. 相邻的兄弟元素
    2. 2.2. 父元素和子元素
  3. 3. 保持一个方向的margin
  4. 4. 延伸阅读
Table of Contents

透析Margin Collapsing

如果不是太粗犷,margin collapsing会是CSS中出现频率很高的一个问题。读完这篇文章,应该彻底明白:

  1. 什么情况下会触发margin collapsing
  2. 有哪些解决方法

这篇文章同时是《CSS内功心法》补充资料,感兴趣的同学可以查看完整的CSS课程

来自W3C的描述:

In CSS, the adjoining margins of two or more boxes (which might or might not be siblings) can combine to form a single margin. Margins that combine this way are said to collapse, and the resulting combined margin is called a collapsed margin.

Adjoining vertical margins collapse, except:

Margins of the root element’s box do not collapse.
If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.

也就是说,只有margin毗邻两个或者多个元素(既可以是相邻节点,也可以是父子节点)才会发生外边距折叠。

margin collapsing 条件

  • margin毗邻(可以是兄弟节点之间也可以是父子节点之间)
  • 两个或两个以上

什么是毗邻?

adjoining margin具体说明如下:

Two margins are adjoining if and only if:

  • both belong to in-flow block-level boxes that participate in the same block formatting context
  • no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
  • both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
    • top margin of a box and top margin of its first in-flow child
    • bottom margin of box and top margin of its next in-flow following sibling
    • bottom margin of a last in-flow child and bottom margin of its parent if the parent has ‘auto’ computed height
    • top and bottom margins of a box that does not establish a new block formatting context and that has zero computed ‘min-height’, zero or ‘auto’ computed ‘height’, and no in-flow children

两个margin adjoining有且仅满足:

  • 属于同一个BFC
  • 相邻margin之间没有行盒、clearance、padding、border分隔(注意看加粗的文字)

    这里我的理解是,相邻的兄弟元素的bottom margintop margin之间不可能有clearancepaddingborder

    注意,行盒是可行的,只需要将它们设置成inline-block

  • 垂直方向margin相邻,满足如下四种情况中的一个:

    • 一个元素的top margin和它的第一个子元素的top margin
    • 一个元素的bottom margin和其相邻的兄弟元素的top margin
    • 父元素高度必须是auto,那么父元素的bottom margin和其最后一个子元素的bottom margin
    • 一个元素自己的top marginbottom margin也可以发生折叠:没有创建BFC、min-height0(默认值)、height0或者auto,并且没有子元素。

阻止margin collapsing的方法

按照上述条件,想想有哪些可行的方法阻止margin collapsing。我的基本思路是:

  • 脱离正常文档流
  • 增加line box, padding,border
  • 设置高度(仅适合父元素的bottom margin和最后一个子元素的bottom margin折叠的情况)。

为了方便区分,这里按照相邻的兄弟元素父子元素简单说明一下:

相邻的兄弟元素

目前考虑到的能应用于实际的方法有两个:

  • 脱离文档流: position设置为absolute(fixed)或者float
  • line boxdisplay设置inline-block

    额外补充,行盒是什么:

    The rectangular area that contains the boxes that form a line is called a line box.

    When several inline-level boxes cannot fit horizontally within a single line box, they are distributed among two or more vertically-stacked line boxes. Thus, a paragraph is a vertical stack of line boxes.

    When an inline box exceeds the width of a line box, it is split into several boxes and these boxes are distributed across several line boxes. If an inline box cannot be split (e.g., if the inline box contains a single character, or language specific word breaking rules disallow a break within the inline box, or if the inline box is affected by a white-space value of nowrap or pre), then the inline box overflows the line box.

父元素和子元素

父元素和子元素的问题相对简单:

  • 为父元素创建BFC

    关于BFC的详细内容,出门右拐谈谈BFC

  • 设置padding 或者 border

    当然也可以为子元素设置inline-block,但是这样会阻止子元素的margin collapsing,也会产生其他副作用,不推荐。很多前辈已经强调过margin collapsing并非bug,它的设计是为了纠正元素之间因为粗心同时设置了margin而偏离预期的错误

  • 指定高度:局限性太大,而且只能解决bottom margin的问题,不推荐

所以其实用得最多的还是BFC和设置padding。举两个经典的例子:

clearfix

.cf:before,
.cf:after {
    content: " ";
    display: table;
}
.cf:after {
    clear: both;
}
/**
 * For IE 6/7 only
 * Include this rule to trigger hasLayout and contain floats.
 */
.cf {
    *zoom: 1;
}

你可能见过这样的写法(打开bootstrap的源码看看),其中的妙处在于display: table

个人不推荐这种写法,推荐display: block,我们很多时候并不希望去掉margin collapsing

再看另外一个例子:

.parent{
  margin-top: -1px;
  padding-top: 1px;
}

这个也是经常用到的

保持一个方向的margin

最后一个建议:尽量统一仅按照margin-top或者margin-bottom来写

延伸阅读

The very latest clearfix reloaded