理解CSS中BFC

BFC(Block Formatting Context)是Web页面中盒模型布局的CSS渲染模式。它的定位体系属于常规文档流。摘自W3C

浮动,绝对定位元素,inline-blocks, 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

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

例如,请看下面的HTML

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

一个新的BFC可以通过给容器添加任何一个触发BFC的CSS样式,如overflow: scroll, overflow: hidden, display: flex, float: left,或者 display: table来创建。

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

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

container {
    overflow: hidden;
}

你可以自由使用除了overflow: hidden之外的样式声明。

BFC中的盒子对齐

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

BFC

简单来说,在上图中我们可以看到,所有属于同一个BFC的盒子都左对齐(左至右的格式),他们的左外边框紧贴着包含块的左边框。在最后一个盒子里我们可以看到尽管那里有一个浮动元素(棕色)在它的左边,另一个元素(绿色)仍然紧贴着包含块的左边框。关于为什么会发生这种情况的原理将会在下面的文字环绕部分进行讨论。

BFC导致的外边距折叠

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

为了去理解它,让我们来思考一下下面这个例子。

BFC

在上图中我们看到在红色盒子(一个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。这就是被称为外边距折叠。当兄弟元素的外边距不一样时,将以最大的那个外边距为准。

使用BFC来防止外边距折叠

这一开始听起来可能有些困惑,因为我们在前面讨论了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

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

使用BFC来包含浮动

一个BFC可以包含浮动。很多时候我们会碰到这种情况,一个容器里有浮动元素。由于这个原因,容器元素没有高度,它的浮动孩子将会脱离页面的常规流。我们通常使用清除浮动来解决这个问题,最受欢迎的方法是使用一个clearfix的伪类元素。但我们同样可以通过定义一个BFC来达到这个目的。

BFC

让我们来看看一个例子:

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

它的CSS:

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

在上面的这个案例中,父容器将不会有任何的高度,它将不会包含已经浮动的子元素。为了解决这个问题,我们通过添加overflow: hidden,在容器中创建一个新的BFC。经过修改过的CSS为:

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

现在,这个容器将包含浮动的子元素,它的高度将扩展到可以包含它的子元素,在这个BFC,这些元素将会回到页面的常规文档流。

使用BFC来防止文字环绕

有时候一个浮动div周围的文字环绕着它(如下图中的左图所示)但是在某些案例中这并不是可取的,我们想要的是外观跟下图中的右图一样的。为了解决这个问题,我们可能使用外边距,但是我们也可以使用一个BFC来解决。

BFC

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

BFC

图中的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不再需要移位,最终将会环绕在浮动元素的下方,因此出现了那样的情况。这就解释了为什么即使在浮动元素存在时,段落也将紧贴在包含块的左边框上,还有为什么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).

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

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

在多列布局中使用BFC

如果我们正在创建的一个多列布局占满了整个容器的宽度,在某些浏览器中最后一列有时候将会被挤到下一行。会发生这样可能是因为浏览器舍入(取整)了列的宽度使得总和的宽度超过了容器的宽度。然而,如果我们在一个列的布局中建立了一个新的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; 
}

在CodePen 上DEMO的运行结果:

现在即使容器的宽度会有轻微的变化,但是布局也不会中断。当然,这并不是多列布局的最好选择,但它是防止最后一列下滑问题的一种方法。Flexbox在这种情况下可能是一个更好的解决方案,但是这应该要说明一下在这些情况下元素是如何表现的。

总结

我希望这篇文章已经向你展示了BFC的相关特性,以及他们如何影响元素在页面上的视觉定位。所有的例子都展示了它们在实际案例中的应用,这应该会使得他们更加清晰。

如果你有任何东西想要补充,请在评论中让我们知道。如果你想要更深入的讨论的话,请务必在W3C上的讨论话题中留言。

本文根据@Ritesh Kumar的《Understanding Block Formatting Contexts in CSS》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://www.sitepoint.com/understanding-block-formatting-contexts-in-css/

一半一半

大连民族大学在校生,网络工程专业。喜欢编程,对JS的特效编写感兴趣,享受从努力到获取成果的整个过程。在工作室团队的带领下,为人一半安静,一半逗比。

如需转载,烦请注明出处:http://www.w3cplus.com/css/understanding-block-formatting-contexts-in-css.html

返回顶部