Skip to content

BFC 块级格式化上下文 ​

问题 ​

BFC 是什么?它能解决哪些布局问题?

结论 ​

理解路径 ​

  1. 页面布局不是只计算单个元素的位置,还要处理元素之间的相互影响,例如块盒排列、浮动避让、内部浮动高度计算和 margin 折叠。
  2. CSS 2.1 / CSS 2.2 的视觉格式化模型把这些相互影响抽象为 formatting context。
  3. 常见 formatting context 包括 BFC(Block Formatting Context,块级格式化上下文)和 IFC(Inline Formatting Context,行内格式化上下文)。
  4. BFC 负责一类块级布局规则,核心影响块级盒排列、浮动参与高度计算、外部浮动避让和垂直 margin 折叠。
  5. 因此 BFC 常被用来解释清除浮动、两栏布局和 margin 折叠隔离。

定义 ​

CSS 布局不是只有一套规则。浏览器会根据盒子的类型、display、定位、浮动等条件,把元素放进不同的 formatting context 中;每种 formatting context 都有自己的布局规则,用来决定内部盒子如何排列,以及它们如何和浮动、margin 等布局关系相互影响。

BFC 是 Block Formatting Context,块级格式化上下文,是 formatting context 的一种。它可以理解成一块按块布局规则计算的独立布局区域,不是某个 CSS 属性。

BFC 主要影响三类布局关系:

创建 BFC 的情况 ​

常见会创建 BFC 的情况包括:

  • 根元素 <html>。
  • 浮动元素:float 不是 none。
  • 绝对定位或固定定位元素:position: absolute / fixed。
  • display: inline-block。
  • 表格相关盒:table-cell、table-caption,以及由 display: table 等隐式创建的匿名表格盒。
  • 块级盒设置 overflow,且值不是 visible 或 clip。
  • display: flow-root。
  • contain: layout、contain: content、contain: paint。
  • flex item:display: flex / inline-flex 容器的直接子元素,且该子元素本身不是 flex、grid 或 table 容器。
  • grid item:display: grid / inline-grid 容器的直接子元素,且该子元素本身不是 flex、grid 或 table 容器。
  • 多列容器:column-count 或 column-width 不是 auto,包括 column-count: 1。
  • column-span: all。

display: flex / display: grid 建立的是 flex / grid formatting context,不应直接说成“这个元素创建 BFC”。但 flex item、grid item 在满足条件时会创建 BFC。

布局规则 ​

  1. 在 BFC 中,普通流里的块级盒会沿 一个接一个排列。
  2. 同一个 BFC 的普通流中,相邻块级盒之间的垂直距离由 margin 决定,。
  3. 普通流块盒的 margin box 外边缘会与包含块的 border box 外边缘相接触;即使存在浮动,块盒本身的边界仍按包含块计算,通常是内部行盒被浮动挤开。
  4. 建立 BFC 的元素在计算自动高度时,会包含其内部浮动元素。
  5. 正常流中建立 BFC 的元素,其 border box 不会与同一上下文中的浮动元素 margin box 重叠。
  6. BFC 可以理解成一个 。

应用场景 ​

包含内部浮动 ​

父容器没有建立 BFC 时,内部浮动不会撑开父容器的自动高度;建立 BFC 后,父容器计算自动高度时会包含内部浮动。

现代写法优先使用:

css
.container {
  display: flow-root;
}

overflow: hidden 也能创建 BFC,但会改变溢出行为,可能裁剪阴影、下拉菜单或绝对定位溢出内容。

避开外部浮动 ​

下面的 .content 通过 display: flow-root 建立 BFC,因而会整体避开左侧浮动的 .sidebar。早期的自适应两栏布局常利用这个特性:

css
.sidebar {
  float: left;
  width: 160px;
}

.content {
  display: flow-root;
}

隔离 margin 折叠 ​

相邻块级盒的垂直 margin 可能折叠。要隔离折叠,不是随便把某个 p 变成 BFC,而是把其中一个元素放进新的 BFC 容器。

html
<p class="first">第一个段落</p>
<div class="bfc-wrapper">
  <p class="second">第二个段落</p>
</div>
css
.first,
.second {
  margin-block: 24px;
}

.bfc-wrapper {
  display: flow-root;
}

垂直 margin 折叠如何计算 ​

普通块流中相邻块盒的垂直 margin 可能折叠,折叠后的距离不是简单相加。

css
p {
  margin-block-start: 10px;
  margin-block-end: 15px;
}
html
<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>

这些空段落的上下 margin 会连续折叠在一起。全部都是正 margin 时,折叠结果取最大值,所以 AAA 和 BBB 之间的间距通常是 15px,不是多个段落 margin 的总和。

折叠计算要注意:

  • 全部为正值时,取最大正值。
  • 全部为负值时,取绝对值最大的负值。
  • 正负混合时,最大正值和最小负值相加。
  • flex/grid 项目之间不会按普通块流规则发生这种垂直 margin 折叠。

什么是 margin 重叠问题?如何解决? ​

两个块级元素在垂直方向上相邻时,它们的 margin 会合并(折叠)为一个,取较大值,而不是相加。这只发生在垂直方向,水平方向不折叠;浮动元素和绝对定位元素不会折叠。

折叠计算规则:

  • 两者都为正数:取较大值;
  • 一正一负:正值减去负值的绝对值;
  • 两者都为负数:取绝对值较大的那个(结果仍为负)。

折叠发生的两类情况及解决方法:

(1)兄弟元素之间折叠

css
/* 两个相邻块,margin 会折叠 */
.a { margin-bottom: 20px; }
.b { margin-top: 30px; }
/* 两者之间实际间距为 30px,不是 50px */

解决:把其中一个变成 BFC 的成员(如 display: inline-block、float),或把其中一个包裹在新建 BFC 的容器里。

(2)父子元素之间折叠(父元素没有 border/padding 隔离时)

css
.parent { /* 没有 border 或 padding */ }
.child { margin-top: 30px; }
/* 子元素的 margin-top 会"穿透"父元素,与父元素外部的 margin 折叠 */

解决:

  • 父元素加 overflow: hidden(触发 BFC);
  • 父元素加透明边框 border: 1px solid transparent;
  • 父元素加 padding-top;
  • 子元素改用 padding-top 或改变显示方式。

常见误区 ​

  • BFC 是布局上下文,不是 CSS 属性。
  • display: flex / grid 不等于创建 BFC;它们建立的是 flex / grid formatting context。
  • overflow: hidden 可以创建 BFC,但不应只为了 BFC 默认使用它。
  • BFC 只能隔离部分布局影响,不会隔离继承、定位、绘制等所有 CSS 影响。

Demo ​

html
<section class="card">
  <img class="thumb" src="/images/example.png" alt="">
  <p>内部图片设置 float 后,父容器仍需要包住它。</p>
</section>
css
.card {
  display: flow-root;
  border: 1px solid #94a3b8;
}

.thumb {
  float: left;
  width: 120px;
  margin-right: 16px;
}

这个例子验证的是:建立 BFC 的容器在计算自动高度时会包含内部浮动。

参考来源 ​