先记住这个答案
会合并。空块元素上下两个外边距之间没有任何东西隔开,没有内容、没有 padding、没有 border,高度也是 0,CSS 就把它们看作相邻外边距,合成一个,值取较大的那个。更容易忽略的是,这个合并后的外边距还会继续和上一个兄弟的 margin-bottom、下一个兄弟的 margin-top 合并,所以空元素有时看起来像完全不占空间。只要给它加上内容、padding、border、height 或 min-height 中的任何一个,合并就被打断。
- 空块的上下外边距会合并,取较大值
- 合并后还会和前后兄弟的外边距接着合并
- 正负混合时:最大正值加上绝对值最大的负值
- 内边距、边框或内容可阻止合并
算一算这段间距到底多大
条件渲染经常留下空的包裹元素,比如一个没有数据时内容为空的提示区。
<p class="a">第一段</p>
<div class="empty"></div>
<p class="b">第二段</p>
<style>
.a { margin-bottom: 16px; }
.empty { margin: 20px 0 30px; } /* 自身合并成 30px */
.b { margin-top: 10px; }
/* 最终两段之间的距离:max(16, 20, 30, 10) = 30px,不是 16 + 50 + 10 = 76px */
</style>这里有四个外边距挨在一起:.a 的下边距、.empty 的上下边距、.b 的上边距。它们中间没有任何内容或边框隔开,所以会一次性合并成一个,取最大值 30px。
如果把 .empty 删掉,两段之间是 max(16, 10) = 16px。也就是说,这个空元素只让间距从 16px 变成了 30px,而不是多出 50px。
什么情况能阻止它合并
空块的上下外边距能合并,前提是两者之间「什么都没有」。所以只要往中间塞点东西就行。
- 能打断合并的写法
- 给空块加
padding-top: 1px或者border-top: 1px solid transparent,上下边距就被隔开了,不再自身合并。设一个非 0 的height或min-height也可以。放进一个文字、一个行内元素,也会让它不再是「空」的。 还有一类办法是改变布局方式。把父容器改成display: flex并设flex-direction: column,子项的外边距不会合并;浮动、绝对定位、display: inline-block的元素也不参与外边距合并。现在更推荐的写法是父级用 Flex 或 Grid 的gap控制间距,子元素不写外边距,从根上避开这个问题。 给空元素自身设overflow: hidden或display: flow-root,让它创建 BFC,也能阻止自身合并,CSS 2.1 的规则里明确排除了创建新 BFC 的盒子。但它的上边距仍会和上一个兄弟合并,下边距仍会和下一个兄弟合并,只是上下两个不再合成一个。
负外边距时怎么算
合并不只是「取最大」。规范规定的算法是:所有正的外边距取最大值,所有负的外边距取绝对值最大的那个,两者相加。
比如一个空块写 margin: 20px 0 -30px,自身合并后是 20 + (-30) = -10px,下面的元素会往上移 10px。如果全是负数,就取绝对值最大的负值。实际项目里用负外边距做重叠效果时,旁边再有个空元素,间距就会算错,按这个规则就能推回来。
容易答错的地方
- 以为空元素的上下外边距会相加
margin: 20px 0 30px的空块只贡献 30px,而且还可能被前后更大的外边距吸收掉。用空div撑间距时,这是间距对不上最常见的原因。- 以为合并只发生在兄弟元素之间
- 外边距合并有三种情况:相邻兄弟之间、父元素和第一个或最后一个子元素之间、空块自己的上下之间。只记住第一种,遇到空元素和父子穿透时就解释不了。
- 以为
display: none的元素也会参与 display: none的元素不生成盒子,外边距也就不存在,不参与合并。visibility: hidden的元素仍然生成盒子,它的外边距照常参与,如果它是空的,也会自身合并。
面试官还会怎么问?
空元素的外边距会不会穿出父元素?
会。如果父元素没有 padding、border,也没有创建 BFC,空子元素的外边距可能先自身合并,再和父元素的外边距合并,最后穿到父元素外面。给父元素加 padding 或 border,或者用 display: flow-root 让父元素创建 BFC,就能挡住。
用空 div 做占位间隔,怎么保证它真的占那么高?
不要靠外边距,直接给它设 height。比如间隔 24px 就写 height: 24px,它是真实占位的高度,不会和任何外边距合并。更好的做法是去掉占位元素,用父级的 gap 或子元素的外边距管理间距。
水平方向的外边距会合并吗?
不会。外边距合并只发生在块方向,也就是普通横排文字下的上下方向。左右外边距总是相加。如果页面用 writing-mode: vertical-rl 改成竖排,块方向变成左右,合并也会跟着发生在左右方向上。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。