深入理解BFC

250 阅读5分钟

BFC全称Block Formatting Context,中文为块格式化上下文, BFC没有一个清晰直观的定义,但是它有一些特性/功能,我们可以利用触发BFC的特性,给元素设置CSS样式,得到想要的效果。

首先,我们来看官方文档是怎么描述BFC的:

CSS规范中对 BFC 的描述

浮动,绝对定位元素,非块盒的块容器(例如,inline-blocks,table-cells)和'overflow'不为'visible'的块盒会为它们的内容建立一个新的块格式化上下文

在一个块格式化上下文中,盒在竖直方向一个接一个地放置,从包含块的顶部开始。两个兄弟盒之间的竖直距离由'margin'属性决定。同一个块格式化上下文中的相邻块级盒之间的竖直margin会合并

在一个块格式化上下文中,每个盒的left外边(left outer edge)挨着包含块的left边(对于从右向左的格式化,right边挨着)。即使存在浮动(尽管一个盒的行盒可能会因为浮动收缩),这也成立。除非该盒建立了一个新的块格式化上下文(这种情况下,该盒自身可能会因为浮动变窄)

MDN 对 BFC 的描述

一个块格式化上下文(block formatting context) 是Web页面的可视化CSS渲染出的一部分。它是块级盒布局出现的区域,也是浮动层元素进行交互的区域。

元素满足以下条件中任何一个符合,就创建了BFC:

  • 根元素或其它包含它的元素
  • 浮动元素 (元素的 float 不是 none)
  • 绝对定位元素 (元素具有 position 为 absolute 或 fixed)
  • 内联块 (元素具有 display: inline-block)
  • 表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
  • 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
  • 具有overflow 且值不是 visible 的块元素,如scroll/auto
  • display: flow-root 仅触发BFC,不会产生其他副作用。
  • column-span: all 应当总是会创建一个新的格式化上下文,即便具有 column-span: all 的元素并不被包裹在一个多列容器中。

一个块格式化上下文包括创建它的元素内部所有内容,除了被包含于创建新的块级格式化上下文的后代元素内的元素。

块格式化上下文对于定位 (参见 float) 与清除浮动 (参见 clear) 很重要。定位和清除浮动的样式规则只适用于处于同一块格式化上下文内的元素。浮动不会影响其它块格式化上下文中元素的布局,并且清除浮动只能清除同一块格式化上下文中在它前面的元素的浮动。

以上内容很难直接看懂,但是我们可以通过实践以上触发BFC条件,去体会BFC的功能与特性。

BFC 的功能与特性

BFC可以让外部元素不被内部元素影响。

功能一:父元素触发BFC 包住浮动的子元素

<div class="father">
  <div class="son">
  </div>
</div>
.father {
  border:2px solid red;
  min-height:10px;
}
.son {
  background:green;
  width:400px;
  height:100px;
  float:left;
}

可以看出,给子元素设置了浮动,父元素就包不住子元素了:

image-20220317150709078.png

如何使父元素能够包住子元素,按照标准给父元素设置触发BFC的条件:

方法一:浮动float:left;

方法二:绝对定位position:absolute;

方法三:display:inline-block;或者display:table-cell或者display:flow-root;

方法四:overflow:auto;或者overflow:scroll;

image-20220317151203497.png

因此,如果一个元素使用以上任一方法就触发了BFC,那么它就能包住浮动的子元素。其高度也会发生变化。

注意:不是清除浮动!

给一个元素设置BFC可以使其包住浮动的子元素,这不是清除浮动,以下才是清除浮动:

<div class="father clearfix">
  <div class="son">
  </div>
</div>
​
.clearfix::after {
  content: '';
  display:block;
  clear:both;
}

触发BFC可能会影响其他元素的属性的副作用,这不是我们想要的效果,如果想要清除浮动请用clearfix。

特性:竖直margin合并

在同一个BFC中的两个子元素(无浮动),会在竖直方向上margin会合并。

<div class="father">
  <div class="son1"></div>
  <div class="son2"></div>
</div>
.father {
  border:2px solid red;
  min-height:10px;
  display: flow-root;  /* 触发BFC  */
}
.son1 {
  background:green;
  width:400px;
  height:100px;
  margin:10px;
}
.son2 {
  background:orange;
  width:400px;
  height:100px;
  margin:10px;
}

image-20220317152325615.png

解决办法:在任意一个兄弟元素外面再套一个触发了BFC的div

<div class="father">
  <div class="son1_bfc">
    <div class="son1"></div>
  </div>
  <div class="son2"></div>
</div>
.son1_bfc{
  display:flow-root;
}

image.png

功能二:让两个相邻的元素界限分明

希望左右布局的两个元素,左边的元素触发了BFC,右边的main就会和aside不清不楚。

<aside>侧边栏</aside>
<main>主要内容</main>
aside {
  border:5px solid green;
  width:100px;
  height:600px;
  float:left;  /* 触发BFC */
}
main {
  border:10px solid orange;
  height:600px;
}

image-20220317154019314.png

让main元素触发BFC,设置overflow:scroll或者overflow:auto或者display: flow-root

image-20220317154242357.png

总结

一个元素拥有一些属性之后,它就触发了BFC,由此带来了一些特性:

  • 可以包住浮动的子元素,同时改变了元素的高度。
  • BFC里的元素会竖直margin合并
  • 可以让两个相邻元素界限分明

一些常用的触发BFC条件:(满足任一即可)

  • 元素浮动(元素的 float 不是 none)
  • 元素绝对定位(元素的 position 为 absolute / fixed)
  • 元素行内块 display:inline-block;
  • overflow 值不为默认值visible 的块元素
  • 弹性元素(display为 flex / inline-flex元素的直接子元素

参考阅读

BFC MDN文档

BFC特性 张鑫旭