BFC 是什么?怎么触发?能解决哪些问题?
一句话回答
BFC(块级格式化上下文)是页面上一块独立的布局区域,里面的块级盒子按规则垂直排列,内外布局互不影响。常见触发方式有 overflow 不为 visible、display: flow-root、浮动、绝对定位、inline-block,以及 flex / grid 的子项。它能解决三类问题:阻止父子外边距合并、包含浮动子元素(清除浮动)、不被旁边的浮动元素覆盖。现在推荐用专门的 display: flow-root 触发,没有副作用。
详细解析
BFC 的布局规则
- 内部的块级盒子从上到下一个接一个排列
- 同一个 BFC 里相邻块级盒子的垂直 margin 会合并
- 每个盒子的左外边缘紧贴包含块的左边缘(从右到左书写时是右边),即使旁边有浮动元素也是如此,只是里面的行盒会缩短、让文字绕开浮动
- BFC 的区域不会和浮动元素重叠
- 计算 BFC 的高度时,浮动的子元素也参与计算
- BFC 是隔离的容器,里面的元素不会影响外面的布局,反之亦然
后面三条就是 BFC 能解决问题的根源。
怎么触发
| 方式 | 副作用 |
|---|---|
display: flow-root |
没有,专门为创建 BFC 设计,推荐 |
overflow: hidden / auto / scroll |
内容溢出会被裁剪或出现滚动条 |
float: left / right |
元素脱离普通流 |
position: absolute / fixed |
脱离文档流 |
display: inline-block |
变成行内级,受空白字符、基线对齐影响 |
| flex / grid 容器的直接子项 | 需要父元素本来就是 flex / grid |
表格单元格、display: table-cell 等 |
表格相关的布局行为 |
根元素 html |
整个页面本身就是一个 BFC |
注意 flex、grid 容器自己创建的是 flex / grid 格式化上下文,不是 BFC,但它的子项(本身是块容器时)会各自创建 BFC。overflow: clip 不会创建 BFC,这是它和 overflow: hidden 的区别之一。另外 contain: layout / paint、多列容器等也会创建 BFC,日常用得少。
外边距合并的三种情况
合并规则:两个都是正数取较大值,都是负数取绝对值较大的,一正一负则相加。
- 相邻兄弟:上面元素的
margin-bottom和下面元素的margin-top合并 - 父子之间:父元素没有 border、padding、行内内容把它和第一个子元素隔开时,子元素的
margin-top会"穿透"到父元素外面;底部同理(父元素还要没有固定高度) - 空的块元素:自身没有内容、padding、border、高度时,它的
margin-top和margin-bottom合并成一个
只有普通流中块级盒子的垂直 margin 会合并。浮动、绝对定位、inline-block 元素,以及 flex / grid 子项之间都不会。
能解决的三个问题
- 父子 margin 合并:给父元素创建 BFC,子元素的 margin 就留在父元素里面
- 包含浮动(清除浮动):子元素全部浮动时父元素高度塌陷为 0;父元素成为 BFC 后,高度会把浮动子元素算进去
- 不被浮动元素覆盖:浮动元素旁边的普通块会钻到浮动元素下面(只有文字绕开);让它创建 BFC,它就会缩窄自己,紧贴浮动元素排列,这就是 float 两栏布局的原理,见 两栏、三栏布局
代码示例
<style>
.parent {
display: flow-root; /* 去掉这一行,父元素高度只算 .main 的 60px,浮动元素会溢出到父元素外 */
background: #e0f2fe;
}
.float-child {
float: left;
width: 100px;
height: 100px;
background: #fca5a5;
}
.main {
display: flow-root; /* 去掉这一行,.main 的背景会钻到浮动元素下面 */
height: 60px;
background: #bbf7d0;
}
</style>
<div class="parent">
<div class="float-child"></div>
<div class="main">右侧自适应区域</div>
</div>
面试官可能追问
清除浮动还有哪些方法?和 BFC 有什么区别?
另一种是在父元素末尾用伪元素加 clear: both,也就是常说的 clearfix:.clearfix::after { content: ""; display: block; clear: both; }。它的原理是让一个末尾的块元素排到浮动元素下方,从而把父元素撑开,并没有创建 BFC。display: flow-root 一行就能同时解决包含浮动和 margin 穿透,是更现代的写法。
两个相邻兄弟的 margin 合并了,给其中一个设 flow-root 有用吗?
没用。两个兄弟仍然处在父元素所在的同一个 BFC 里,它们之间的 margin 照样合并。BFC 只隔离"内部和外部",要阻止兄弟合并,得把其中一个包进一个新的 BFC 容器,或者干脆只给一个元素设 margin。
为什么 flex 布局里没有 margin 合并的问题?
外边距合并是块级格式化上下文里的规则。flex 容器内部是 flex 格式化上下文,子项之间的 margin 不合并,容器和子项之间也不合并。这也是很多人改用 flex 后就不再遇到这个问题的原因。
易错点
overflow: hidden能触发 BFC,但会裁剪溢出内容(比如下拉菜单、阴影),不是没有代价- 外边距合并只发生在垂直方向,水平 margin 永远不合并
- flex / grid 容器本身不是 BFC,它的子项才是
AI 模拟面试官
用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮
这道题你掌握了吗?
选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。
学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。