什么是 BFC
什么是 BFC
BFC(Block Formatting Context,块级格式化上下文)是 CSS 渲染页面时的一个概念,指页面中一块独立的布局区域。
W3C 的解释是:BFC 决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。当涉及到可视化布局时,BFC 提供了一个环境,HTML 元素在其中按照块级布局的规则进行排列。
通俗地说:BFC 是一个独立的布局环境,内部子元素的布局不会影响外部,外部也不会影响它。
需要注意:BFC 不是某个 CSS 属性,它是渲染过程中的一种布局区域。我们无法直接"设置 BFC",而是通过某些 CSS 属性值的副作用来创建(开启)它。
开启 BFC 能解决什么问题
一个元素创建了新的 BFC 后,会获得以下三个特性:
- 包含内部浮动:就算其子元素浮动,元素自身高度也不会塌陷(能"包住"浮动子元素);
- 排除外部浮动:自己不会被其他浮动元素覆盖,而是紧贴在浮动元素旁边排布;
- 阻止 margin 合并:该元素与其子元素之间、该元素与外部元素之间的垂直 margin 不再合并。
注意第 3 点的边界:开启 BFC 阻止的是该元素与子元素之间、该元素与外界的 margin 合并;但 BFC 内部相邻子元素之间的 margin 仍然会正常合并,这是块级布局的默认行为。
哪些元素会创建 BFC
满足以下任一条件的元素会创建新的 BFC:
- 文档的根元素(
<html>,页面加载后就默认存在); - 浮动元素:
float不为none; - 定位元素:
position: absolute、fixed或sticky; display: inline-block;- 表格类:
display: table-cell(HTML 中默认是td、th);display: table-caption(默认是caption);display: table、table-row、table-row-group、table-header-group、table-footer-group、inline-table——它们会让内部生成匿名 table cell,间接创建 BFC;
- 块级盒子且
overflow不为visible且不为clip(即auto、scroll、hidden等); display: flow-root(最推荐,见下文);- flex 项:
display: flex / inline-flex容器的直接子元素(前提是该子元素自身不是 flex、grid 或 table 容器); - grid 项:
display: grid / inline-grid容器的直接子元素(前提同上); - 多列容器:
column-count或column-width不为auto; column-span: all的元素(即使没有包裹在多列容器中);contain: layout、content或strict的元素;container-type不为normal的元素(容器查询元素)。
注意区分:flex/grid 容器本身创建的是 flex 格式化上下文(FFC)或 grid 格式化上下文(GFC),严格来说不是 BFC——只是行为类似(内部 margin 不合并、不与外部浮动重叠)。创建 BFC 的是它们的子元素。
三大典型场景
1. 高度塌陷(包含内部浮动)
父元素没有高度、子元素又全部浮动时,父元素高度会塌陷为 0:
<div class="container">
<div class="float-item">我是浮动的子元素</div>
</div>
.container {
border: 2px solid #333;
/* 子元素浮动后,这里高度塌陷为 0,边框"包不住"子元素 */
}
.float-item {
float: left;
width: 200px;
height: 100px;
background-color: #74b9ff;
}
给父元素开启 BFC,即可让它"包住"浮动的子元素:
.container {
display: flow-root; /* 高度不再塌陷 */
}
2. 不被浮动元素覆盖(排除外部浮动)
右侧元素会被左侧浮动元素部分覆盖(文字环绕效果的由来):
<div class="float-box">左浮动</div>
<div class="normal-box">正常块级元素</div>
.float-box {
float: left;
width: 150px;
height: 100px;
background-color: #ffeaa7;
}
.normal-box {
/* 未开启 BFC:会与浮动元素重叠排布,文字环绕 */
/* 开启 BFC:整体排布在浮动元素右侧,不被覆盖 */
overflow: hidden; /* 方式之一 */
}
开启 BFC 后,.normal-box 会识别浮动元素的存在,紧贴其右边缘排布。这也是"两栏自适应布局"的经典原理。
3. 父子 margin 合并(margin 塌陷)
子元素的 margin-top 会"穿透"到父元素外面:
<div class="parent">
<div class="child">子元素</div>
</div>
.parent {
width: 300px;
background-color: #55efc4;
/* 未处理时:child 的 margin-top 会跑到 parent 外面 */
}
.child {
margin-top: 50px;
}
给父元素开启 BFC,父子的 margin 就不会再合并:
.parent {
display: flow-root;
}
最佳实践:display: flow-root
开启 BFC 的方式很多,但各有副作用:
| 方式 | 副作用 |
|---|---|
overflow: hidden | 会裁剪溢出内容、隐藏滚动 |
float: left | 元素自身脱离文档流 |
display: inline-block | 改变元素显示类型 |
position: absolute | 脱离文档流 |
display: flow-root | 无副作用,专为此而生 |
display: flow-root 的含义就是"创建一个块级盒子,且它是一个新的 BFC 根",语义明确、无任何布局副作用,是现代浏览器中开启 BFC 的首选。主流浏览器均已支持。
总结
- BFC 是一块独立的布局区域,内部元素不影响外部,外部也不影响内部;
- BFC 的三大作用:包含内部浮动、排除外部浮动、阻止 margin 合并;
- 阻止 margin 合并仅限于"该元素与子元素/外界"之间,BFC 内部子元素之间的 margin 仍会合并;
- 创建 BFC 的方式很多(根元素、浮动、绝对定位、overflow、表格类、flex/grid 项等),但首选
display: flow-root——它无副作用、语义清晰; - 不要为了开启 BFC 而开启,只在需要解决上述三类布局问题时使用。