跳到主要内容

BFC 与格式化上下文

什么是 BFC

BFC(Block Formatting Context,块级格式化上下文)是 CSS 视觉格式化模型中的一种独立布局环境。可以把它理解为页面中的一块“隔离布局区域”:

  • BFC 内部的块级盒子按照正常文档流在垂直方向排列。
  • BFC 内部的布局通常不会影响外部元素,外部浮动也不会侵入该区域。
  • BFC 会包含其内部的浮动元素,并参与高度计算。

“互不影响”是一种便于理解的概括,并非完全隔离。元素的继承、尺寸约束以及层叠关系等仍然可能跨越 BFC 边界。

BFC 的布局规则

  1. 块级盒子垂直排列

    同一 BFC 中,块级盒子从上到下依次排列。

  2. 相邻块的外边距可能折叠

    同一 BFC 中,相邻块级元素的垂直 margin 可能发生折叠;不同 BFC 之间不会发生垂直外边距折叠。

  3. BFC 不与浮动盒子重叠

    BFC 会避开同级浮动元素,其可用宽度会根据浮动元素占用的空间进行收缩。

  4. BFC 会包含内部浮动元素

    计算 BFC 高度时,其内部的浮动元素也会被计算在内,因此可用于解决父元素高度塌陷。

触发 BFC 的常见条件

以下情况会创建新的 BFC:

  • 根元素 <html>
  • float 的值不是 none
  • positionabsolutefixed
  • displayinline-block
  • displayflow-root
  • displaytable-celltable-caption 等表格相关值。
  • overflow 的值不是 visibleclip,例如 hiddenautoscroll
  • displayflexinline-flex 的元素,其直接子元素会建立 BFC。
  • displaygridinline-grid 的元素,其直接子元素会建立 BFC。
  • contain 包含 layoutcontentpaint
  • 多列容器,例如设置了 column-countcolumn-width 的元素。

💡 如果只是为了显式创建 BFC,优先使用 display: flow-root。它能清晰表达布局意图,也不会像 overflow: hidden 一样意外裁剪溢出内容。

BFC 的典型应用

1. 清除浮动,避免父元素高度塌陷

子元素浮动后会脱离普通文档流,父元素可能无法被其撑开。让父元素创建 BFC 后,计算高度时会包含浮动子元素。

.parent {
display: flow-root;
}

.child {
float: left;
}

传统方案常使用 overflow: hidden,但它可能裁剪阴影、下拉菜单等溢出内容,因此通常更推荐 flow-root

2. 避免父子元素的垂直 margin 折叠

父元素与第一个或最后一个普通流子元素之间,垂直 margin 可能发生折叠。让父元素建立 BFC 可以阻止这种跨边界折叠。

.parent {
display: flow-root;
}

.child {
margin-top: 20px;
}

不过,如果只是需要父子间距,使用父元素的 paddinggap 往往语义更明确。

3. 避免元素被浮动元素覆盖

普通块级元素的边框盒可能延伸到浮动元素下方,而其文本内容会环绕浮动元素。让该元素创建 BFC 后,它会避开浮动元素,形成常见的两栏布局。

.aside {
float: left;
width: 200px;
}

.main {
display: flow-root;
}

现代项目通常优先使用 Flexbox 或 Grid 实现两栏布局;BFC 更适合处理已有浮动布局或理解浏览器行为。

容易混淆的概念

BFC 与包含块(Containing Block)

  • BFC 决定盒子如何参与布局,以及浮动、外边距折叠等行为。
  • 包含块 是计算元素尺寸和位置的参照区域,例如绝对定位元素会寻找其定位包含块。

两者可能由同一个元素产生,但不是同一概念。

BFC 与层叠上下文(Stacking Context)

  • BFC 主要处理布局。
  • 层叠上下文 主要处理元素在 z 轴上的覆盖顺序。

创建 BFC 不一定创建层叠上下文,创建层叠上下文也不一定创建 BFC。

总结

BFC 的核心价值可以归纳为三点:

  • 包含浮动:让容器正确计算浮动子元素的高度。
  • 隔离 margin 折叠:阻止垂直外边距跨 BFC 边界折叠。
  • 避开外部浮动:使布局区域不与浮动盒子重叠。

在现代 CSS 中,需要主动创建 BFC 时,通常优先选择:

.container {
display: flow-root;
}