BFC 那是什么? 为什么听起来和KFC那么像?

399 阅读6分钟

块级格式化上下文(Block Formatting Context)也被称为BFC,是Web页面的可视CSS渲染的一部分,是块级盒子的在布局过程中产生的区域,也是浮动元素与其他元素交互的区域。这个区域具有独立的渲染规则和特性。

通常情况下,我们会为了定位和清除浮动来创建新的BFC,而不是去改变布局。因为它能:

  • 包含内部浮动。
  • 阻止元素重叠。
  • 阻止外边距重叠。

创建BFC

我们可以通过以下方法来创建BFC:

  • 文档的根元素(<html>)。
  • 浮动元素(即 float 值不为 none 的元素)。
  • 绝对定位元素(position值为 absolute 或 fixed 的元素)。
  • 行内块元素(display 值为 inline-block 的元素)。
  • 表格单元格(display 值为 table-cell,HTML 表格单元格默认值)。
  • 表格标题(display值为 table-caption,HTML 表格标题默认值)。
  • 匿名表格单元格元素(display值为 table(HTML 表格默认值)、table-row(表格行默认值)、table-row-group(表格体默认值)、table-header-group(表格头部默认值)、table-footer-group(表格尾部默认值)或 inline-table)。
  • overflow 值不为 visible 或 clip 的块级元素。
  • display 值为 flow-root 的元素。
  • contain 值为 layoutcontent 或 paint 的元素。
  • 弹性元素(display 值为 flex 或 inline-flex 元素的直接子元素),如果它们本身既不是弹性、网格也不是表格容器。
  • 网格元素(display 值为 grid 或 inline-grid 元素的直接子元素),如果它们本身既不是弹性、网格也不是表格容器。
  • 多列容器(column-count 或 column-width 值不为 auto,且含有 column-count: 1 的元素)。
  • column-span 值为 all 的元素始终会创建一个新的格式化上下文,即使该元素没有包裹在一个多列容器中。

需要注意的是,HTML 是根元素,也是最顶级的BFC。而且弹性/网格容器(display:flex/grid/inline-flex/inline-grid)建立新的弹性/网格格式化上下文,除布局之外,它与区块格式化上下文类似。弹性/网格容器中没有可用的浮动子级,但排除外部浮动和阻止外边距重叠仍然有效。

包含内部浮动

BFC 能让浮动元素和周围的内容等高。因为浮动会让浮动元素脱离正常的文档流,从而导致布局错位。但 BFC 能有效读取浮动元素的高度,可以有效防止浮动元素溢出并影响其他部分,从而解决高度塌陷问题。

接下来我们将通过一个例子来详细解释。

在例子中,我们首先让<div> 元素浮动,并给它添加 border 效果。现在 <div> 里的内容已经在浮动元素周围浮动起来了。由于浮动的元素比它旁边的元素高,所以 <div> 的边框穿出了浮动。正如我们先前所言,浮动会脱离文档流,所以<div> 的 background 和 border 仅仅包含了内容,不包含浮动。

使用 overflow:auto

在创建包含浮动元素的 BFC 时,通常是在父元素添加 overflow:auto 属性或是除默认的 overflow:visible 以外的值。<div> 元素就会变成布局中的小型布局,从而将子元素包含进去。

使用 overflow 来创建新的 BFC,是因为 overflow 属性会告诉浏览器如何去处理溢出的内容。如果仅仅是为了创建 BFC ,使用 overflow 可能会出现一些你不希望的结果——滚动条或阴影。需要注意的是,对于之后接手的开发者来说,可能并不明白为什么要使用 overflow ,所以最好添加一点注释。

使用 display:flow-root

一个新的 display 属性的值,可以无副作用的创建 BFC ,在父级元素中使用 display-flow-root 可以创建新的 BFC。

<div> 元素设置 display-flow-root 属性后,就会让 <div> 中的所有内容参与到这个 BFC 中,浮动的内容就不会从底部溢出。

代码如下:

<section>
  <div class="box">
    <div class="float">我是浮动的盒子!</div>
    <p>我是容器内的内容。</p>
  </div>
</section>
<section>
  <div class="box" style="overflow:auto">
    <div class="float">我是浮动的盒子!</div>
    <p>我是 <code>overflow:auto</code> 容器内部的内容。</p>
  </div>
</section>
<section>
  <div class="box" style="display:flow-root">
    <div class="float">我是浮动的盒子!</div>
    <p>我是 <code>display:flow-root</code> 容器内部的内容。</p>
  </div>
</section>
section {
  height: 150px;
}
.box {
  background-color: rgb(224, 206, 247);
  border: 5px solid rebeccapurple;
}
.box[style] {
  background-color: aliceblue;
  border: 5px solid steelblue;
}
.float {
  float: left;
  width: 200px;
  height: 100px;
  background-color: rgba(255, 255, 255, 0.5);
  border: 1px solid black;
  padding: 10px;
}

结果也显而易见:

Snipaste_2025-01-30_16-12-11.png

阻止元素重叠

下面这个例子,将使用 display:flow-root 和浮动实现双列布局,因为在正常的文档流中建立的 BFC 不会与元素本身所在的 BFC 中的任何浮动元素的外边距重叠。

<section>
  <div class="float">试试重新调整这个外部浮动元素的大小</div>
  <div class="box"><p>普通</p></div>
</section>
<section>
  <div class="float">试试重新调整这个外部浮动元素的大小</div>
  <div class="box" style="display:flow-root">
    <p><code>display:flow-root</code></p>
    <p></p>
  </div>
</section>
section {
  height: 150px;
}
.box {
  background-color: rgb(224, 206, 247);
  border: 5px solid rebeccapurple;
}
.box[style] {
  background-color: aliceblue;
  border: 5px solid steelblue;
}
.float {
  float: left;
  overflow: hidden; /* resize:both 所必需的样式 */
  resize: both;
  margin-right: 25px;
  width: 200px;
  height: 100px;
  background-color: rgba(255, 255, 255, 0.75);
  border: 1px solid black;
  padding: 10px;
}

结果如下:

Snipaste_2025-01-30_16-28-41.png

请注意,与 inline-block 需要设置 width:<percentage> 不同的是,在示例中,我们不需要设置右侧 <div> 元素的宽度。

防止外边距折叠

我们可以利用创建新的 BFC 来避免相邻元素之间的外边距折叠。

接下来的这个示例,将创造两个相邻的 <div> 元素,每个元素在垂直方向上都有10px的外边距。我们在为了解外边距折叠得情况下都会认为两个盒子之间的间距会是20px。很可惜,由于外边距折叠,它们之间只有10px。

<div class="blue"></div>
<div class="red"></div>
.blue,
.red {
  height: 50px;
  margin: 10px 0;
}

.blue {
  background: blue;
}

.red {
  background: red;
}

Snipaste_2025-01-30_16-50-31.png

如何防止外边距折叠

在这个示例中,想要解决外边距折叠这个问题非常简单,只需要对其中一个盒子创建新的 BFC 即可。在这里,我们选择创建新的 <div> 元素,将第二个盒子包裹进去,来创建一个新的 BFC 来解决外边距折叠。

<div class="blue"></div>
<div class="outer">
  <div class="red"></div>
</div>
.blue,
.red {
  height: 50px;
  margin: 10px 0;
}

.blue {
  background: blue;
}

.red {
  background: red;
}

.outer {
  overflow: hidden;
  background: transparent;
}

Snipaste_2025-01-30_16-58-16.png 不难发现两个盒子之间的距离比之前大了一些。当然,除了包裹一层 <div> 元素这个方法,还可以选择向其中一个盒子添加 float:leftdiaplay:inline-block 属性来创建新的 BFC 。

总结

BFC 的存在,解决了浮动所带来的一系列问题,让开发者可以更加灵活地控制网页的布局,从而创造出更加丰富的页面。学习和理解 BFC,有助于解决常见的一些CSS布局问题。

新年创作不易,看到最后的话,还希望点个小小的赞。