什么是外边距重叠(margin collapsing)?重叠的结果是什么?如何解决?

两个或多个毗邻的普通流中的块元素垂直方向上的 margin 会折叠。

1. 两个或多个

说明其数量必须是大于一个,又说明折叠是元素与元素间相互的行为,不存在 A 和 B 折叠、B 没有和 A 折叠的现象。

2. 毗邻

是指没有被非空内容、padding、border 或 clear 分隔开,说明其位置关系。

注意一点:在没有被分隔开的情况下,一个元素的 margin-top 会和它普通流中的第一个子元素(非浮动元素等)的 margin-top 相邻;只有在一个元素的 height 是 "auto" 的情况下,它的 margin-bottom 才会和它普通流中的最后一个子元素(非浮动元素等)的 margin-bottom 相邻。

3. 垂直方向

是指具体的方位,只有垂直方向的 margin 才会折叠,水平方向的 margin 不会发生折叠。

4. 折叠计算规则

  • 两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。
  • 两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。
  • 两个外边距一正一负时,折叠结果是两者的相加的和。

5. 如何解决外边距重叠?

<div style="width:100px; background:#930; height:100px; margin-bottom:10px;"></div>
<div style="width:100px; background:#f30; height:100px; margin-top:10px;"></div>

如果触发上下 <div> 元素的 BFC,是无法避免 margin 折叠的——这与很多人的直觉相反。

6. 真正使上下 margin 不折叠的方法

  • 浮动元素、inline-block 元素、绝对定位元素的 margin 不会和垂直方向上其他元素的 margin 折叠(注意这里指的是上下相邻的元素)。
  • 创建了块级格式化上下文(BFC)的元素,不和它的子元素发生 margin 折叠(注意这里指的是创建了 BFC 的元素和它的子元素)。

触发 BFC 的因素:float(除了 none)、overflow(除了 visible)、display(table-cell / table-caption / inline-block)、position(除了 static / relative)。

补充:

  • 相邻元素不发生折叠的因素是触发 BFC 因素的 子集——如果为上下相邻的元素设置 overflow: hidden,虽然触发了 BFC,但上下元素的上下 margin 还是会发生折叠。
  • BFC 不是万能的,它只是让元素本身能正确计算宽高,并不能阻止相邻兄弟元素的 margin 折叠。
  • 实战建议:写代码时尽量统一方向(如全部使用 margin-top 或 margin-bottom),避免一正一负叠加造成意想不到的间距。