[翻译] 理解 CSS 中的 BFC

BFC (Block Formatting Context,即块格式化上下文) 是网页的可视化 CSS 盒模型渲染的一部分,它的定位方式(Positioning Scheme)属于常规文档流(normal flow)。摘自W3C:

Floats, absolutely positioned elements, inline-blocks, table-cells, table-captions, and elements with ‘overflow’ other than ‘visible’ (except when that value has been propagated to the viewport) establish new block formatting contexts.

浮动、绝对定位、行内块级、table-cells、table-captions 等元素的 overflow 的属性值不为 visible(除了这个值已经被传到了视口的时候)将创建一个新的块级格式化上下文。

上面的引述几乎总结了 BFC 是怎样形成的,但是让我们以另一种方式来重新定义以便能更好的理解。一个 BFC 是至少满足下列条件中的任何一个条件的 HTML 盒子:

  • float 的值不为 none
  • position 的值不为 static 或者 relative
  • display 的值为 table-cell, table-caption, inline-block, flex, 或者 inline-flex 中的其中一个;
  • overflow 的值不为 visible

创建一个 BFC(Creating a Block Formatting Context)

一个 BFC 可以被显式的触发。所以,如果想要创建一个新的 BFC,只需要给它添加上面提到的任何一个 CSS 样式就可以了。

例如,下面的 HTML 代码:

<div class="container">
    Some Content here
</div>

一个新的 BFC 可以通过给容器添加任何一个触发 BFC 的 CSS 样式创建。例如添加 overflow: scrolloverflow: hiddendisplay: flexfloat: left 或者 display: table 属性值到容器上。

  • display:table 可能会产生一些问题;
  • overflow:scroll 可能会显示不必要的滚动条;
  • float:left 将会把元素置于容器的左边,其他元素环绕着它;
  • overflow:hidden 将会剪切掉溢出的元素;

所以,每当想要创建一个新的 BFC 的时候,我们可以根据需求选择最好的样式条件。为了一致性,我在这篇文章给出的所有例子中全部使用了 overflow: hidden 属性值。

container {
    overflow: hidden;
}

当然,你可以使用除了 overflow: hidden 之外的其他任何处罚 BFC 的样式声明。

BFC 中的盒子对齐(Alignment of Boxes in a Block Formatting Context)

W3C 这样描述:

In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

在块格式化上下文中,每个盒子的左外边缘与包含块的左边缘接触(从右向左的布局格式,则右边缘接触)。即使在浮动的情况下也是如此(尽管盒子中的盒子可能由于浮动而收缩),除非盒子建立了一个新的块格式化上下文(在这种情况下,由于浮动,盒本身可能变得更窄)。

bfc-1

在 BFC 中,每个盒子的左外边框紧挨着包含块的左边框(从右到左的格式,则为紧挨右边框)。即使存在浮动也是这样的(尽管一个盒子的边框会由于浮动而收缩),除非这个盒子的内部创建了一个新的 BFC 浮动,盒子本身将会变得更窄)。

BFC 导致的外边距折叠(A Block Formatting Context Causes Collapsing Margins)

在常规文档流中,盒子都是从包含块的顶部开始一个接着一个垂直堆放。两个兄弟盒子之间的垂直距离是由他们个体的外边距所决定的,但不是他们的两个外边距之和。

为了去理解它,让我们来看看一下下面这个例子:

bfc-2

在上图中我们看到在红色盒子(div 元素)中包含两个绿色的兄弟元素(p 元素),一个 BFC 已经被创建。

<div class="container">
    <p>Sibling 1</p>
    <p>Sibling 2</p>
</div>

相应的 CSS 代码:

.container {
    background-color: red;
    overflow: hidden; /* creates a block formatting context */
}
p {
    background-color: lightgreen;
    margin: 10px 0;
}

理论上两个兄弟元素之间的边距应该是来两个元素的边距之和(20px),但它实际上为10px。这就是被称为外边距折叠(collapsing margins)。当兄弟元素的外边距不一样时,将以最大的那个外边距为准。

演示地址:Adjacent Margins Collapsing in a Block Formatting Context

使用 BFC 来防止外边距折叠(Using a Block Formatting Context to Prevent Margin Collapse)

这个问题听起来可能有些困惑,因为在前面讨论了 BFC 导致外边距折叠的问题。但我们必须清楚一点,相邻盒子的垂直外边距折叠只有它们是在同一 BFC 时才会发生。如果他们属于不同的BFC,它们之间的外边距将不会折叠。所以通过创建一个新的 BFC 就可以防止外边距折叠。

在前面的例子中添加第三个兄弟元素,它的 HTML 代码将变为:

<div class="container">
    <p>Sibling 1</p>
    <p>Sibling 2</p>
    <p>Sibling 3</p>
</div>

对应的 CSS 代码:

.container {
    background-color: red;
    overflow: hidden; /* creates a block formatting context */
}
p {
    background-color: lightgreen;
    margin: 10px 0;
}

结果将会和上面的一样。将会有一个边距折叠,三个兄弟元素将会以垂直距离为 10px 分开。会这样是因为三个 p 元素都是属于同一个 BFC。

现在再来修改第三个兄弟元素,使得它属于另一个新的 BFC。HTML 代码是:

<div class="container">
    <p>Sibling 1</p>
    <p>Sibling 2</p>
    <div class="newBFC">
       <p>Sibling 3</p>
    </div>
</div>

CSS 代码为:

.container {
    background-color: red;
    overflow: hidden; /* creates a block formatting context */
}
p {
    margin: 10px 0;
    background-color: lightgreen;
}
.newBFC {
    overflow: hidden;  /* creates new block formatting context */
}

现在,在显示的结果将会有改变了:

bfc-3

当第二和第三个兄弟元素属于不同的 BFC 时,在它们之间将不会有任何外边距折叠,在下面的 DEMO 中能很明显看到。

演示地址:Using a New Block Formatting Context to Prevent Margin Collapse

使用 BFC 来包含浮动(Using a Block Formatting Context to Contain Floats)

一个 BFC 可以包含浮动。在开发的时候,我们会使用一个容器里有浮动元素的处理方案。它的浮动子节点将会脱离页面的常规流,由于这个原因,容器元素没有高度。通常我们使用清除浮动来解决这个问题,最常用的方法是使用一个 clearfix (的伪类元素清除浮动)。但我们也可以通过定义一个 BFC 来实现。

bfc-3

让我们来看看一个例子:

<div class="container">
    <div>Sibling</div>
    <div>Sibling</div>
</div>

CSS 代码:

.container {
    background-color: green;
}
.container div {
    float: left;
    background-color: lightgreen;
    margin: 10px;
}

在上面的这个例子中,父容器(container)将不会有任何的高度,它不会包含已经浮动的子元素(浮动子节点将会脱离页面的常规流)。我们可以通过给父容器添加 overflow: hidden,在容器中创建一个新的 BFC,来解决这个问题。经过修改过的 CSS 代码为:

.container {
    overflow: hidden; &#x2F;* creates block formatting context *&#x2F;
    background-color: green;
}
.container div {
    float: left;
    background-color: lightgreen;
    margin: 10px;
}

现在,这个容器将包含浮动的子元素,它的高度是它包含的子元素的高度的和,通过创建的这个 BFC,这些子元素将会回到页面的常规文档流。

演示地址:Using a Block Formatting Context to Clear Floats

使用 BFC 来防止文字环绕(Using Block Formatting Contexts to Prevent Text Wrapping)

有时候一个浮动 div 周围的文字会环绕着它(如下图中的左图所示),但是在某些情况下这样又是可取的,我们想要的是外观跟下图中的右图一样的。为了解决这个问题,我们可以使用(比较大的)外边距,但是我们也可以通过创建一个 BFC 来解决。

bfc-4

首先让我们来了解文字为什么会环绕(浮动元素)。为此我们必须要知道当一个元素浮动时盒子模型是如何工作的。这就是我之前在讨论 BFC 对齐时留下的那部分。让我们从下图来了解发生了什么。

bfc-5

图中的 HTML 代码大概是这样的:

<div class="container">
    <div class="floated">Floated div</div>
    <p>Quae hic ut ab perferendis sit quod architecto,dolor debitis quam rem provident aspernatur tempora expedita.</p>
</div>

在上图中的整个黑色区域为 p 元素。正如所看到的,这个 p 元素并没有移动,但是它却出现在浮动元素的下方。p 元素的文本行(line boxes)进行了移位。因此,行框水平地缩小,以便为浮动元素腾出空间。

随着文字的增加,因为 line boxes 不再需要移位,最终将会环绕在浮动元素的下方,因此出现了那样的情况。这就解释了为什么即使在浮动元素存在时,段落也将紧贴在包含块的左边框上,还有为什么 line boxes 会缩小以容纳浮动元素。

如果能够转换整个 p 元素,那么就可以解决这个环绕的问题了。

在去解决之前,让我们再看一下 W3C 标准上是怎么描述的:

In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

在块格式化上下文中,每个盒子的左外边缘与包含块的左边缘接触(从右向左的布局格式,则右边缘接触)。即使在浮动的情况下也是如此(尽管盒子中的盒子可能由于浮动而收缩),除非盒子建立了一个新的块格式化上下文(在这种情况下,由于浮动,盒本身可能变得更窄)。

根据这一点,如果 p 元素建立了一个新的 BFC,那么它将不再触及容器块的左边缘。可以通过简单的给 p 元素添加 overflow: hidden来实现。这样通过创建一个新的 BFC 就解决了文字环绕在浮动元素周围的问题。

演示地址:Preventing Text Wrapping around a Float Using a Block Formatting Context

在多列布局中使用 BFC(Using Block Formatting Contexts in Multi-column Layouts)

如果我们要创建一个占满了整个容器宽度的多列布局,在某些浏览器中最后一列有时候将会被挤到下一行。会出现这种情况可能是因为浏览器就算列的宽度时,总和的宽度超过了容器的宽度。然而,如果我们在一个列的布局中建立了一个新的 BFC,它将会在前一列填充完之后的后面占据所剩余的所有空间。

让我们使用多列布局中的三列布局来作为例子。

HTML 代码:

<div class="container">
    <div class="column">column 1</div>
    <div class="column">column 2</div>
    <div class="column">column 3</div>
</div>

CSS 代码:

.column {
    width: 31.33%;
    background-color: green;
    float: left;
    margin: 0 1%;
}
.column:last-child {
    float: none;
    overflow: hidden; 
}

演示地址:Using a Block Formatting Context to make a Final Column “fit”

现在即使容器的宽度会有轻微的变化,布局也不会中断(折行)。当然,这并不是处理多列布局的最好选择,但它是防止最后一列折行的一种方法。Flexbox 在这种情况下可能是一个更好的解决方案,但是这种(创建 BFC)处理方式说明了在这种情况下元素是如何表现的。

结论

我希望这篇文章向您展示了 BFC 的相关内容以及它如何影响页面上元素的视觉定位的。文章中的例子展示了实际应用中怎么使用,也希望能够帮助大家更好的理解 BFC。

如果您有什么要补充的,请在评论中告诉我们。如果你想深入研究,请务必回顾一下W3C对这个主题的更详细的讨论

SHARE THIS PAGE

免责声明:本站文章中的观点都是作者个人观点,并没有以任何方式反映他所属机构的意见。

发表评论