探究 BFC 以及闭合浮动原理

Liu Bowen

Liu Bowen / 2018, 三月, 29

块级格式化上下文定义与形成

块级格式化上下文(block formatting context)在 CSS3 中被称为 flow root

块级格式化上下文是指,当元素满足特定条件就会形成一个单独的盒模型渲染模式区域参考一:MDN参考二:W3C)。

默认地,每个视口初始创建一个 BFC(该 BFC 即根元素 BFC ,它也计算后代(不仅仅是子代)浮动元素高度)。除非元素设置特定样式创建新的 BFC 。

特定条件是指:

  1. 根元素,即<html>
  2. position值不为staicrelative(可理解为值为 absolutefixed
  3. float不为none(即不为默认值)
  4. overflow不为visible(即不为默认值)
  5. displaytable-celltable-captioninline-blockflexinline-flex之一

(注:position:sticky 粘性定位是相对定位(relative)和固定定位(fixed)的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。)

当一个元素满足以上条件之一,那么该元素将在普通流(normal floww3c没有文档流一说的)创建一个新的 BFC。创建了块级格式化上下文的元素中的所有内容都会被包含到该 BFC 中(清除浮动的原理)。这里注意 display: table 是不会创建新的 BFC 的,该特性只会创建一个匿名框(参考),而是匿名框中的 display: table-cell 创建新的 BFC。

块级格式化上下文的特点

  1. BFC 是一个独立的 CSS 渲染区域,其内部与外部互不影响
  2. BFC 中元素是按照垂直方向从上至下排列
  3. BFC 中子元素相对于 BFC 元素是左对齐的(从右向左布局,则右对齐)
  4. 不同的块级格式化上下文的 margin 不会叠加,即外边距叠加只发生在同一 BFC 内。(应用:防止外边距叠加)
  5. 块级格式化上下文不会与浮动元素重叠。(常见应用:当二者为兄弟元素时,创建 BFC 可阻止元素被浮动元素覆盖)
  6. 块级格式化上下文中可以包含浮动元素。但 BFC 中是不包含绝对定位 等 BFC 的。(常见应用:父元素添加 .clearfix 类来定义一个 BFC 使该元素计算 BFC 高度时包含其子浮动元素,而不是因为子元素因浮动而脱离普通流,父元素进而“高度坍塌”)

(注:BFC 阻止浮动元素重叠,此时 BFC 与 浮动元素一般是兄弟元素;BFC 清除浮动,此时 BFC 与 浮动元素一般是父子元素)

重要:经试验,在同为脱离普通流的绝对定位和固定定位形成的 BFC 是不会被外层 BFC 包含的。在脱离普通流形成的 BFC 中(左或右浮动,绝对和固定定位)只有浮动元素 BFC 会被外层 BFC 在计算高度时,包含在内。

绝对定位形成的 BFC 与 根元素 BFC: pa

固定定位形成的 BFC 与 根元素 BFC: pa

浮动形成的 BFC 与 根元素 BFC:

pa

在以上特点中有一个问题是,为什么 BFC 可以包含浮动元素,并在计算时包含浮动元素的高度?

原因:参考W3C 计算元素高度,有以下:

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.

其中特别的有,Only floats that participate in this block formatting context are taken into account,简而言之就是只有 BFC 中的浮动元素才会被 BFC 计算高度。

即对于该 BFC 中的普通流,我们可以总结出在创建一个新的 BFC 时,会将其所有的子元素(对于已脱离普通流的元素,仅包含脱离 BFC 的浮动元素)都会被包含在该 BFC (限定在特定的一个 CSS 渲染区域内)中。这也是清除浮动的原理

结论:对于第三点应用,因为有独立的块级格式化上下文可以包裹浮动元素(计算浮动元素高度),所以达到一种将浮动元素“拉回”普通流计算高度的效果。

补充拓展:对于块级非替换元素的高度计算规则(参考三):

Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, and relatively positioned boxes are considered without their offset). Note that the child box may be an anonymous block box.

只有在普通流中的子元素才包含在元素计算高度中。

BFC 中元素布局的特点(W3C BFC):

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 中的每一个盒模型的左外边缘都是左对齐于包含块的左边缘。(若排列格式为从右到左,那么是右对齐)

总结布局特点:

  1. BFC 中元素是按照垂直方向从上至下排列
  2. BFC 中子元素相对于 BFC 元素是左对齐的(从右向左布局,则右对齐)

总结

综上,我们可以将 BFC 理解为元素在满足一定条件后在普通流中形成了一个单独的盒子,这个盒子里面的布局和外面的布局是相互独立,互不影响的。

区分清除浮动和闭合浮动

浮动元素的特点:

  1. 不属于普通流(normal flow)
  2. 可左右移动
  3. 移动至触碰另一个浮动框的边缘或包含块的边缘
  4. 不影响兄弟块级元素的布局,影响兄弟行内元素的文本的排列
  5. 原文档中的普通流就表现得像浮动元素不存在一样,那么当浮动元素高度超过包含块时,包含块高度不会自动适应变化(“高度坍塌”)

最后一点,也是闭合浮动的意义所在,闭合浮动是为了防止高度坍塌,使浮动元素的包含框表现出正常的高度。

清除浮动仅仅是指 clear: both

闭合浮动是指创建一个新的 BFC (即原来浮动元素浮动之前的包含块)包含清除浮动的元素,使其浮动元素被新的 BFC 计算高度,恢复 BFC 的正常高度计算。常用方法是在浮动元素的父元素添加一个.clearfix类来闭合浮动。

闭合浮动的类名 .clearfix 的代码如下:

.clearfix {
  display: inline-block;
  &:after {
    display: block;
    content: '.'; // 点的兼容性优于空格
    height: 0;
    line-height: 0;
    clear: both;
    visibility: hidden;
  }
}

原理:::after伪元素表示创建被选择元素的排在最后的一个子元素。以上代码创建了一个块级元素区域(防止影响包含块中的普通流布局)表示两边清除浮动。因为清除了浮动,那么伪元素将排在浮动元素之下。这样达到一种将浮动元素的位置空出来的效果。对于外层的包含块.clearfix的普通流虽然包含了::after,但因有::after空出位置,.clearfix这时也就间接计算了内部浮动子元素的高度,也就相当于闭合了内部的浮动。

总结:闭合浮动是效果,效果是浮动元素重新被计算高度(W3C 计算 BFC 中元素高度)。清除浮动仅仅是清除了浮动,决定其他非浮动元素的布局。清除浮动仅仅只是闭合浮动的一种方法中的一个步骤。