技术知识文章集合TECHNICAL ARCHIVE · 807 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTHTML

什么是 BFC

所属分类
HTML
文件格式
Markdown
原始路径
HTML/08-什么是 BFC
本文目录8 个章节
  1. 什么是 BFC
  2. 开启 BFC 能解决什么问题
  3. 哪些元素会创建 BFC
  4. 三大典型场景
  5. 最佳实践:display: flow-root
  6. 总结
  7. 参考资料
  8. 补充:现代布局都用 Flex/Grid,还需要学习 BFC 吗?

什么是 BFC

什么是 BFC

BFC(Block Formatting Context,块级格式化上下文)是 CSS 渲染页面时的一个概念,指页面中一块独立的布局区域。

W3C 的解释是:BFC 决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。当涉及到可视化布局时,BFC 提供了一个环境,HTML 元素在其中按照块级布局的规则进行排列。

通俗地说:BFC 是一个独立的块级布局环境,内外浮动的影响以及父子 margin 合并会在这个边界处受到限制。这种“独立”并不意味着内外布局完全互不影响:内部内容仍可撑高容器,容器尺寸仍会影响外部布局。

需要注意:BFC 不是某个 CSS 属性,它是渲染过程中的一种布局区域。我们可以通过某些 CSS 属性值创建(开启)它,其中 display: flow-root 就是专门用于创建 BFC 的方式。

开启 BFC 能解决什么问题

一个元素创建了新的 BFC 后,会获得以下三个特性:

  1. 包含内部浮动:计算容器的自动高度时,会计入参与该 BFC 的浮动元素,避免仅有浮动子元素时的高度塌陷;
  2. 避让外部浮动:普通流中创建 BFC 的块级元素,其边框盒不会与同一外部 BFC 中浮动元素的 margin 盒重叠;空间足够时可排在浮动旁边,不足时可能移到下方;
  3. 阻止父子 margin 合并:该元素与其普通流子元素之间的垂直 margin 不再合并。

注意第 3 点的边界:开启 BFC 阻止的是容器与其普通流子元素之间的 margin 合并,并不保证容器自身与外部兄弟元素的 margin 不合并。例如,两个相邻的 display: flow-root 块级元素,其上下 margin 仍可能合并;BFC 内部相邻普通流块级子元素之间的 margin 也仍可能合并。

哪些元素会创建 BFC

满足以下任一条件的元素会创建新的 BFC:

  • 文档的根元素(<html>,页面加载后就默认存在);
  • 浮动元素:float 不为 none;
  • 绝对定位元素:position: absolute 或 fixed(若其内部采用块布局,则创建 BFC);relative 和 sticky 本身不会创建 BFC;
  • 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 包含 size 或 inline-size 的元素会创建独立格式化上下文(内部采用块布局时为 BFC);仅设置 scroll-state 不会因此创建 BFC。

注意区分:flex/grid 容器本身创建的是 flex 格式化上下文(FFC)或 grid 格式化上下文(GFC),严格来说不是 BFC。它们的直接布局项之间不会发生 margin 合并;但布局项内部若仍采用普通块布局,其内部块级元素之间仍可能合并 margin。普通块布局的 flex/grid 项会为自己的内容创建 BFC;若布局项自身也是 flex/grid 容器,则创建相应的格式化上下文。

三大典型场景

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专门创建 BFC,不额外引入裁剪、滚动或浮动/绝对定位

display: flow-root 的含义就是"创建一个块级盒子,且它是一个新的 BFC 根"。只需要创建 BFC、又想保留普通块布局时,应优先使用它,而不是借助 overflow: hidden 等附带行为。但它并非完全不改变布局:包含浮动、阻止父子 margin 合并等正是预期的变化。

总结

  1. BFC 是独立的块级布局环境,但不意味着内外布局完全互不影响;
  2. BFC 的三大作用:包含内部浮动、避让外部浮动、阻止父子 margin 合并;
  3. 创建 BFC 不会自动阻止容器与外部兄弟元素的 margin 合并,内部相邻普通流块级子元素的 margin 也仍可能合并;
  4. 创建 BFC 的方式很多(根元素、浮动、绝对定位、overflow、表格类、flex/grid 项等);只需要创建 BFC 时,首选 display: flow-root;
  5. 不要为了开启 BFC 而开启,只在需要解决上述三类布局问题时使用。

参考资料

补充:现代布局都用 Flex/Grid,还需要学习 BFC 吗?

现代布局减少了“靠 BFC 修补浮动布局”的需求,但不意味着 BFC 已经过时。

以前与现在的区别

以前经常用 float 拼两栏、多栏布局,因此还需要额外处理父容器高度塌陷、浮动影响旁边内容等问题。“清除浮动”“开启 BFC”也就成了常见的布局技巧。

现在这类布局通常直接使用更适合的布局方式:

布局方式常见用途
Flex以一个主轴为中心排列元素,如导航栏、按钮组、两栏布局,以及内容对齐、空间分配
Grid同时控制行和列,如卡片网格、整体页面分区

Flex/Grid 提供了专门的排列、对齐和空间分配机制,不必再靠浮动拼出这些布局,也就减少了为此处理浮动高度塌陷等问题的需求。W3C:Flex 布局概述

BFC 仍然有哪些用途?

需要理解,但不必把它当作日常布局的主工具。 它仍然适用于以下场景:

  1. 普通文档流中的父子 margin 合并:文章正文、内容容器等不一定使用 Flex/Grid,仍可能需要通过 display: flow-root 阻止父子 margin 合并。
  2. 图片浮动、文字环绕:这是 float 本来擅长的用途,Flex/Grid 的并排布局不能直接替代文字沿图片轮廓或边缘环绕的效果;包含浮动时仍可使用 BFC。
  3. 维护老项目:理解浮动、清除浮动和 BFC,有助于读懂、修复已有布局。

用了 Flex/Grid,就不会发生 margin 合并了吗?

不是。要区分直接布局项与它们内部的普通块级元素。

  • Flex/Grid 的直接布局项之间不会发生 margin 合并。
  • 布局项内部如果仍是普通块布局,其内部相邻块级元素之间仍可能发生 margin 合并。
  • 所以,给外层设置 display: flex 或 display: grid,不意味着所有后代元素的 margin 都不会合并。

例如,一个 Flex 项内部包含两个普通段落,两个段落相邻的上下 margin 仍可能合并。W3C:Flex 容器与格式化上下文

实际开发怎么选择?

页面和组件布局优先用 Flex/Grid;需要保留普通块布局,又要包含浮动或阻止父子 margin 合并时,用 display: flow-root。

BFC 是理解 CSS 布局行为的基础概念,而不是与 Flex/Grid 竞争的另一套页面布局方案。

807 DOCUMENTS · 12 COLLECTIONS
ARCHIVE SEARCH807 篇文章