详解块级格式化上下文(BFC)

时间:2022-04-04 08:13:45

  相信大家和我一样,第一次听到别人说CSS 块级格式化上下文(block formatting context,简称:BFC)的时候一头雾水,为了帮助大家弄清楚块级格式化上下文,我翻阅了W3C的CSS规范,把和块级格式化上下文有关的资料整理了出来。

  先介绍一下普通流,普通流就是我们口中常说的文档流。在CSS 2.1的规范中有两段关于普通流的描述:

Normal flow. In CSS 2.1, normal flow includes block formatting of block-level boxes, inline formatting of inline-level boxes, and relative positioning of block-level and inline-level boxes.

  普通流包括了块格式化,行内格式化,相对定位。

Boxes in the normal flow belong to a formatting context, which may be block or inline, but not both simultaneously. Block-level boxes participate in a block formatting context. Inline-level boxes participate in an inline formatting context.

  在普通流中框属于一种格式化上下文,类型可以是block或者是inline,但不能同时属于这两者。块级框(Block-level boxes)参与在块级格式化上下文中,行内框(Inline-level boxes)参与在行内格式化上下文中。

  根据上面两段话我们可以知道,普通流把不同类型的框划分给了不同的格式化上下文,块级格式化上下文就是其中之一。

  再来看看规范是怎么描述格式化上下文的:

  A formatting context is the environment into which a set of related boxes are laid out.Different formatting contexts lay out their boxes according to different rules.

  格式化上下文是布置一组相关框的环境,不同的格式化上下文,根据不同的规则布置自己的框。

  看了这段定义,我们可以清晰的知道块级格式化上下文是什么了:它是一个布置块级框的环境,这个环境有它自己特有的规则。块级框被普通流划分给了块级格式化上下文,然后块级框按照块级格式化上下文的规则呈现在了页面上。

  块级格式化上下文的规则通过规范的这两句话表达了出来:

In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the 'margin' properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

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.

  在BFC中,块级框在它的容器块中从上往下依次排列。两个块级框之间的垂直距离由margin属性决定,在同一个块级格式化上下文中垂直方向上的margin会被重叠(collapse)

  在BFC中,每个块级框左边紧贴容器块的左边缘(书写从右向左的话,紧贴右边缘)。这种情况即使在有浮动元素存在的情况下也会发生,除非块级框创建一个新的格式化上下文。

  现在我们可以总结一下BFC的特性了:

  1. 如果给一个元素创建了一个BFC,就相当于创建了一个新的环境,环境内和环*中的元素不会相互影响。外边的BFC规则,不会对环境里的块级框产生影响,而容器里面的规则也不会对环*的块级框产生影响,也就是相互隔绝,互不影响。
  2. 块级框的布局是从包含容器的顶部开始的,从上往下依次排列。
  3. 在同一个BFC中,两个相邻的块级元素的垂直margin会发生重叠(collapse)。
  4. 每个块级框的边界都要紧靠包含容器的边界,即使块级框的遇到浮动元素,当块级框创建一个新的BFC的时候,这个特性不生效。

  第一条和第二条非常容易理解,就不细说了。第三条和第四条特性,我们在工作中应该都遇到过,可能不清楚css效果背后的原理,这里给大家细说一下。 先说第三条,请看下面代码,元素A和元素B是相邻的兄弟元素,元素B有个子元素C,元素A的底部margin与元素B的顶部margin发生了重叠,元素C的顶部margin与元素A、B的margin也发生了重叠。因为此时元素A、元素B、元素C都属于同一个BFC中(这个BFC就是根元素所创建),而且他们的垂直margin互相接触了,所以发生了重叠。

  <div id="A" style="background: #f1f2f3; margin-bottom: 20px;">
我是A
</div>
<div id="B" style="background: #f1f2f3; margin-top: 20px;">
<div id="C" style="background: red; width: 30px; height: 20px; margin-top: 20px;">我是C</div>
</div>

  代码效果如下所示

我是A
我是C

  这个时候可以设置overflow: auto让B元素创建一个新的BFC,这样元素A就属于元素B新创建的BFC中,就不会发生margin重叠,而元素A和元素B依然同处于同一个BFC中,所以元素A与元素B依然会发生margin重叠。还有很多别的方式阻止margin重叠,这里只关注和BFC有关的内容。

我是A
我是C

  还有一种特殊的margin重叠也与BFC有关,就是空元素自身的顶部margin和底部margin发生了重叠:    

  <div id="container">
<div style="background: #f1f2f3; height: 20px;">我是占位置的</div>
<div id="emptyElementMarginCollapse" style="margin: 20px 0;"></div>
<div style="background: #f1f2f3; height: 20px;">我是占位置的</div>
</div>

   空元素顶部margin和底部margin各有20px,而实际间距只有20px,并不是40px,是因为margin产生了重叠。大家可以自己试一下,博客园编辑器会自动在空元素中插入一个空格,这里就不做代码演示了。此时只需要在空元素上设置overflow:auto创建一下新的格式化上下文,空元素的margin重叠就会消失。和BFC有关的margin重叠问题就是这些了,接下来我们看第四条特性。

  第四条特性和浮动有关,从表现上来看就是我们常说的文字环绕效果,请看下面代码,一个块级元素里面有两个浮动元素,含有文字的块级框的布局完全无视浮动元素,它的左右边界紧贴在包含容器的边缘:

  <div style="width: 400px;border: 1px solid;">
<div style="float: left;width:100px;height: 100px;background-color: #ddd;">float:left;</div>
<div style="float: right;width:100px;height: 100px;background-color: #ddd;">float:right;</div>
<div style="background-color: #f1f2f3;">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima vero aperiam error expedita, tempore nemo obcaecati ipsam incidunt ipsa fugiat! Doloribus delectus, illum nisi. Maxime labore asperiores, inventore in est.</div>
</div>
float:left;
float:right;
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima vero aperiam error expedita, tempore nemo obcaecati ipsam incidunt ipsa fugiat! Doloribus delectus, illum nisi. Maxime labore asperiores, inventore in est.

  现在让我们在含有文字的块级元素上创建一个新的BFC

<div style="width: 400px;border: 1px solid;">
<div style="float: left;width:100px;height: 100px;background-color: #ddd;">float:left;</div>
<div style="float: right;width:100px;height: 100px;background-color: #ddd;">float:right;</div>
<div style="background-color: #f1f2f3;overflow: auto;">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima vero aperiam error expedita, tempore nemo obcaecati ipsam incidunt ipsa fugiat! Doloribus delectus, illum nisi. Maxime labore asperiores, inventore in est.</div>
</div>
float:left;
float:right;
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima vero aperiam error expedita, tempore nemo obcaecati ipsam incidunt ipsa fugiat! Doloribus delectus, illum nisi. Maxime labore asperiores, inventore in est.

  正如规范中所说的一样,一个块级框新建了一个BFC之后,它遇到浮动元素就不会紧贴父元素的边缘了。

  BFC的特性都介绍完了,接下来该介绍如何创建BFC了,这里我给大家整理了一下,格式化上下文的创建方式:

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

  BFC的创建方式有很多种,最常用的就是通过设置overflow属性和设置绝对定位来创建,因为设置overflow属性对整体布局的影响比较小。

  看到这里,相信大家对于BFC不在陌生,对BFC有了一个清晰的认知,不过内容还没结束,浮动元素与BFC真的只有这点关系嘛?回想一下浮动塌陷问题的解决方案,其中有一个方案通过给父容器设置overflow:hidden来解决浮动塌陷的问题,因为设置overflow:hidden会创建一个新的BFC,那这个方案的背后是不是与BFC也有关系呢?没错,这个的确和BFC有关系,但是这并不是BFC的特性,而是height设置为auto的特性,它的特性受到了BFC所影响,CSS2.1 规范中有这样一段话:

  In certain cases, the height of an element that establishes a block formatting context is computed as follows: 

(此处省略三段) 

  In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge, then the height is increased to include those edges. Only floats that participate in this block formatting context are taken into account, e.g., floats inside absolutely positioned descendants or other floats are not.

  在某些情况下,建立了BFC的元素的高度应该按照以下规则计算:如果元素含有浮动的子元素,这些子元素底边缘在父元素底边缘的下面,那么父元素的高度提升到可以包含这些子元素。只有参与了BFC的浮动元素才会考虑,在绝对定位和浮动元素内部的浮动元素不会考虑。

  咱们先看代码以及效果:

  <div style="border: 1px solid;">
<div style="background: #f1f2f3; height: 20px; width: 20px; float: left;">
<div style="background: rgb(18, 7, 230); height: 40px; width: 20px; float: left;margin-left: 30px;"></div>
</div>
</div>

  文本编辑器竟然不允许两个浮动元素嵌套,这里我只能用图片来展示效果了,希望大家都能自己运行一下代码,看看效果。

  详解块级格式化上下文(BFC)

详解块级格式化上下文(BFC)

  对比图中的效果,完全印证了规范里所说的内容。一开始最外层div元素内部只有浮动元素,由于浮动元素脱离普通流,导致了高度塌陷,最外层div元素高度为0。然后给最外层div元素设置了overflow:auto,创建了一下新的格式化上下文,然后新的规则生效了。新规则指出,当块级元素height为auto时,如果块级元素内部有浮动元素,并且浮动元素的底边缘比块级容器元素还低,那么提高块级容器元素的高度,直到它可以包含这些浮动元素,所以最外层的div元素的高度变成了20px。最里面蓝色的浮动元素高度为40px,它的位置是在浮动元素内部,属于例外情况,最外层的div元素不需要包含它,所以它的高度不会影响到最外层div的高度。

  到此为止,有关BFC的内容就都讲完了,总结一下,BFC就是普通流中的由一系列规则组成的影响块级框布局的环境,这个环境可以通过设置一些css属性来触发。BFC影响着外边距重叠、浮动塌陷、元素重叠,在写css的时候需要主意一下。希望看完这篇文章的你对于BFC不再是一头雾水。

  

  非常感谢您的阅读,文中如有不对的地方,欢迎指正交流!