【BFC】详解块级格式化上下文(Block Formatting Context)

BFC(block formatting context),块级格式化上下文,大家对这个名词可能比较陌生,但是肯定经常用过, 只是在运用的时候没有意识到这是 BFC 导致的结果。

BFC 定义

 什么是 BFC,W3C 对 BFC 的定义是:

Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.

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 (in which case the box itself may become narrower due to the floats).

借助翻译工具翻译后的解释是:

浮动或绝对定位元素、非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及 overflow 值不为 visiable 的块级盒子,浏览器都会为他们的内容创建新的块级格式化上下文。

在一个块级格式化上下文里,盒子从包含块的顶端开始垂直地依次排列,两个盒子之间的垂直的间隙是由他们的 margin 值所决定的,两个相邻的块级盒子的垂直外边距会发生融合(叠加)。

在块级格式化上下文中,每一个盒子的左外边缘(margin-left)会触碰到容器的左边缘(border-left)(对于从右到左的格式来说,则触碰到右边缘),即使存在浮动也是如此,除非这个盒子创建一个新的块级格式化上下文。

上面出现了几个比较重要的概念,比如:box,block formatting context。block formatting context 就是“块级格式化上下文”的意思。同时在 W3C 里发现还有 inline formatting context,所以我们先弄清楚 formatting context 是个什么东西。

formatting context 是 W3C CSS2.1 规范中的一个概念,它是页面中的渲染区域块,有一套渲染规则,决定了其子元素将如何定位,以及和其他元素的关系和相互作用。

最常见的 formatting context 有 block fomatting context(简称BFC)和 Inline formatting context(简称IFC),CSS2.1 中只有 BFC 和 IFC,CSS3 中还增加了 GFC 和 FFC。

box 又是什么东西呢?其实我们一点不陌生,因为现在的 CSS 布局就是建立在盒模型基础之上的,box 是 CSS 布局的基本单位。直观点来说,就是一个页面是由很多个元素组成,元素的类型和 display 属性,决定了这个元素的box 类型,不同类型的 box, 会应用不同的 formatting context(一个决定如何渲染文档的容器),因此 box 内的元素会以不同的方式渲染。

常见的盒子类型:

  • block-level box —— display 属性为 block、list-item、table 的元素,会生成 block-level box,并且应用 block fomatting context。
  • inline-level box —— display 属性为 inline、inline-block、inline-table 的元素,会生成 inline-level box,并且应用 inline formatting context。

总结一下:BFC 就是“块级格式化上下文”的意思,应用了 BFC 的元素就是一个独立的盒子,它规定了内部的 block-level box 如何布局,并且与这个独立盒子里的布局不受外部影响,也不会影响到外面的元素。

BFC 特性

  1. 内部的 box 会在垂直方向,从顶部开始依次地排列渲染。
  2. box 垂直方向的距离由 margin 决定。属于同一个 BFC 的两个相邻 box 的 margin 会发生叠加。
  3. 每个 box 元素的 margin-left,与包含块 box 的 border-left 相接触(对于从左往右的格式化,否则相反),即使存在浮动也是如此。
  4. BFC 的区域不会与 float box 叠加。
  5. BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然。
  6. 计算 BFC 的高度时,浮动元素也参与计算。

BFC 触发

上面介绍了 BFC 的定义,那么如何触发 BFC 呢?满足下面任一条件的元素,会触发 BFC:

  • 浮动元素,float 除 none 以外的值
  • 绝对定位元素,position(absolute,fixed)
  • display 为以下其中之一的值 inline-blocks,table-cells,table-captions
  • overflow 除了 visible 以外的值(hidden,auto,scroll)

实际中你会发现,display: table 也会触发 BFC。但其实 display: table 的元素本身并不触发 BFC,而是由它产生的包含 display:table-cell 的匿名框触发了 BFC。对于 display: table 的元素,产生 BFC 的是匿名框而不是 display: table 的元素。

在 CSS3 中,BFC 叫做 Flow Root,并增加了一些触发条件:

  • display 的 table-caption 值
  • position 的 fixed 值,其实 fixed 是 absolute 的一个子类,因此在 CSS2.1 中使用这个值也会触发 BFC ,只是在 CSS3 中更加明确了这一点。

值得注意的是,BFC 并不是元素,而是满足上述条件的元素触发的渲染机制,因此,是上面这些元素触发了 BFC,而它们本身并不是 BFC ,这个概念需要区分清楚。

BFC 应用

接下我们看下怎么运用 BFC,在哪些场景可以用到 BFC,已经能解决什么问题。

1、解决 margin 叠加问题

每个 p 元素之间的 margin 为 50px,此时发生了外边距叠加。要解决这个叠加问题,即让每个 p 元素之间的 margin 是 100px,应该怎么办?我们可以给 p 元素添加一个父元素,让它触发 BFC,如下:

2、用于布局

从图中我们会发现上面 BFC 的第三个特性,就是元素的左外边距会触碰到包含块容器的做外边框,就算存在浮动也会如此。

那么我们如何解决这个问题呢?看上面 BFC 第四个特性,就是 BFC 不会与浮动盒子叠加,那么我们是不是可以创建一个新的 BFC 来解决这个问题呢:

3、清除 float 浮动,解决高度坍塌

我们先看一个经常碰到的 float 导致父元素高度坍塌的例子:

由于两个子元素浮动的关系,两个 box 已经脱离了父元素的包含块,父元素高度已经塌陷。按照上面特性的第 6 点,如果需要让父元素包含两个 box 子元素,就要闭合浮动,触发父元素的 BFC:

BFC 与 hasLayout

在上面的例子中,我们使用 overflow: auto 触发 BFC ,这种方式和一个属性很像,那就是 zoom 属性,这是针对 IE 的 CSShack。因为 IE6-7 并不支持 W3C 的 BFC,而是使用私有属性 hasLayout。从表现上来说,hasLayout 跟 BFC 很相似,只是 hasLayout 自身存在很多问题,导致了 IE6-7 中一系列的 bug 。触发 hasLayout 的条件与触发 BFC 有些相似,推荐为元素设置 IE 特有的 CSS 属性 zoom: 1 触发 hasLayout,zoom 用于设置或检索元素的缩放比例,值为“1”即使用元素的实际尺寸,使用 zoom: 1 既可以触发 hasLayout 又不会对元素造成其他影响,相对来说会更为方便。

这时我们需要注意一个问题,尽管 hasLayout 有着跟 BFC 相似的特性,但 BFC 和 hasLayout 具有的一些特殊性质可以在现代浏览器和 IE 中同时产生,这样导致了一个元素在不同浏览器间的表现因为 BFC 或 hasLayout 出现差异,开发中很多莫名其妙的问题其实都是因此而产生的。所以,如果一个元素没有触发 BFC ,也要尽量保证它没有触发 hasLayout 。