margin 塌陷
解释相邻块级元素为什么会发生 margin 塌陷,以及常见的规避思路。
#type / concept
#status / growing
#resource / css
#tech / dev / frontend
[!info] related notes
- 所属 MOC: CSS MOC
- 前置概念: 盒模型
- 相关问题: BFC 是什么,有什么作用, 文档流、display 与定位
margin 塌陷
一句话定义
垂直方向上相邻块级元素的外边距不会简单相加,而是按照特定规则合并为一个值,这就是 margin 塌陷(margin collapse)。
核心机制 / 工作原理
CSS 规范规定:在普通块级格式化上下文(BFC)中,垂直方向相邻的块级元素的 margin 会合并(collapse),取较大值而非两者之和。这是 CSS 的设计行为,不是 bug。
触发条件
margin 塌陷只发生在垂直方向(block flow direction),水平方向的 margin 永远不会塌陷。
三种塌陷场景详解
场景一:相邻兄弟元素
.box1 { margin-bottom: 20px; }
.box2 { margin-top: 30px; }
直觉:间距 = 20 + 30 = 50px。实际:30px(取较大值)。
<div class="box1">上</div>
<div class="box2">下</div>
<!-- 两者间距为 30px,不是 50px -->
场景二:父子元素
.parent { margin-top: 0; } /* 无 border/padding */
.child { margin-top: 30px; }
子元素的 margin-top 会”穿透”父元素,变成父元素的 margin-top。
<div class="parent">
<div class="child">内容</div>
</div>
<!-- parent 顶部出现 30px 间距,child 仍在 parent 内顶部 -->
场景三:空块级元素
.empty { margin-top: 20px; margin-bottom: 30px; }
一个没有内容、padding、border 的空块元素,自身的 margin-top 和 margin-bottom 会合并为 30px。
解决方案
1. 创建 BFC(块级格式化上下文)
BFC 内部的元素不会与外部发生 margin 塌陷:
.parent {
display: flow-root; /* 最简洁的创建 BFC 方式 */
}
其他创建 BFC 的方式:overflow: hidden、overflow: auto、display: flex、display: grid 等。
2. 使用 padding 或 border 替代
.parent {
padding-top: 1px; /* 任意非零值 */
}
/* 或 */
.parent {
border-top: 1px solid transparent;
}
padding 和 border 会阻断 margin 塌陷。
3. 改用 Flex 或 Grid 布局
.container {
display: flex;
flex-direction: column;
gap: 20px; /* 用 gap 控制间距,不存在塌陷问题 */
}
Flex/Grid 容器中的子元素不参与 margin 塌陷。
最小例子
<div style="margin-bottom: 20px; background: lightblue;">A</div>
<div style="margin-top: 30px; background: lightcoral;">B</div>
<!-- A 和 B 之间间距 30px,不是 50px -->
<!-- 修复版本 -->
<div style="display: flow-root;">
<div style="margin-bottom: 20px; background: lightblue;">A</div>
<div style="margin-top: 30px; background: lightcoral;">B</div>
</div>
<!-- 现在间距是 50px -->
边界与常见误解
- 误解:margin 塌陷是浏览器 bug。 它是 CSS 规范的正式行为,所有浏览器一致实现。
- 误解:水平方向也会塌陷。 只有垂直方向(block flow direction)才会。
- 误解:Flex/Grid 容器中也会塌陷。 Flex/Grid 容器中的子元素不参与 margin 塌陷。
- 边界:负 margin 也参与塌陷。 合并规则:正正取大、负负取小(绝对值大)、正负则相加。
- 边界:
overflow: hidden创建 BFC 有副作用,可能裁剪内容。推荐使用display: flow-root。
最短记忆方式
垂直相邻块元素的 margin 不一定相加;遇到奇怪间距,先怀疑是不是塌陷了。用 display: flow-root 或 Flex/Grid 布局可解决。