块级格式化上下文(BFC)和层叠上下文

块级格式化上下文(BFC)和层叠上下文困扰了我好一阵子,深入了解后发现这两个东西很像,所以这里就放到一起来总结一下。

一、BFC

BFC 是 Block Formatting Context 的缩写,中文可以翻译成块级格式化上下文。

BFC 是 W3C CSS 2.1 规范中的一个概念,它是一个独立的布局环境,如果一个元素参与了 BFC,或者说这个元素拥有一个 BFC,那么这个元素内部元素如何布局定位,以及这个元素与其它元素的相互作用都要符合 BFC 的规则。
BFC 可以理解为一个箱子,箱子内部的元素再怎么翻江倒海都不会影响到箱子外面的元素,反过来也是一样,箱子外面的元素也不会影响箱子里面的元素。

在详细了解 BFC 之前,我们有必要先了解一些有关 CSS 布局的基本概念。

1 块级元素

w3.org 中的描述:

Block-level elements are those elements of the source document that are formatted visually as blocks(e.g., paragraphs). The following values of the “display” property make an element block-level: “block”, “list-item”, and “table”.

翻译成中文大概的意思是,块状元素是源文档在被可视化布局时,被可视化为块的元素。将元素的 display 属性设置为 “block”、”list-item” 或者 “table” 都可以将元素转化为块级元素。

2 块级盒子

w3.org 中的描述:

Block-level boxes are boxes that participate in a block formatting context. Each block-level element generates a principal block-level box that contains descendant boxes and generated content and is also the box involved in any positioning scheme.

翻译成中文大概的意思是,块级盒子是参与块级格式化上下文的盒子。每一个块级元素都会生成一个包含它后代盒子和生成内容的主要块级盒,并且这个盒子参与了任何定位计算。
也就是说,块级元素会自动生成一个块级盒子,而块级格式化上下文是由块级盒子参与的。

3 BFC 的创建及其规则

w3.org 中关于 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” 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 boxed 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 boxed may shrink due to the floats), unless the box established a new block formatting context (in which case the box itself may become narrower due to the floats).

翻译成中文大概是:

创建

  • 浮动元素绝对定位元素不是块级盒子的块级包含块(比如 inline-block、table-cell、table-caption)和 overflow 值不是 visible 的块级盒子,都会建立一个新的块级格式化上下文。

规则

  • 在一个块级格式化上下文中,盒子从包含块的顶部开始,垂直地一个接一个的排列,相邻(如果相邻的是父元素,那么也会折叠)两个盒子之间的垂直间距由盒子的 margin 属性值决定,在一个块状格式化上下文中相邻的连个块级盒子之间的垂直 margin 是折叠的。
    需要注意的是,参与 BFC 布局的只有普通流 normal flow 中的块级盒,而 float 和绝对定位的元素虽然可以创建自己的 BFC,但是它们不属于普通流(浮动元素和绝对定位元素会脱离普通流),不参与包含它们的 BFC 的布局,同样也不会与其相邻元素(相邻元素是父元素也算)的 margin 进行折叠。

  • 在一个块级格式化上下文中,每个盒子的左外边接触包含块的左边,即使在浮动元素参与的情况下也是如此(浮动元素会覆盖这个盒子),除非这个盒子新建了一个块级格式化上下文。

布局规则整理一下大概为下面 5 条:

  1. 在 BFC 下,内部的 box 会在垂直方向上一个接一个地放置
  2. box 垂直方向的间距由 margin 决定,属于同一个 BFC 的两个相邻 box 的 margin 会发生重叠
  3. 在 BFC 中,每一个盒子的左外边缘会触碰容器的左外边缘
  4. BFC 的区域不会与 float box 重叠(自适应两栏布局)
  5. 计算 BFC 的高度时,浮动元素也参与计算(解决浮动塌陷)

最后两条实际上是利用 BFC 是独立布局区域,不影响其它元素也不受其它元素影响的特性。虽然浮动元素不参与 BFC 内布局,但是仍然是 BFC 中的一部分)

二、层叠上下文

层叠上下文是 HTML 元素的三维概念,可以假象为一条垂直于视窗的 z 轴。当元素创建了重叠上下文时,就可以说这个元素就拥有了一个 z 轴,它内部的子元素发生重叠时,会依据自身属性按照优先级顺序占用 z 轴(重叠上下文)上的空间,优先级最大的元素,排在最上面,离用户也最近。

1 层叠顺序

层叠顺序就是层叠上下文 内部元素 占用 z 轴空间时的优先级顺序,它是一套排序的规则。
stacking order
这张图是从张大神的博客中偷来的。

注意:

  1. 排在最底下的 background/border 属于当前层叠上下文,即参与层叠排序的所有元素的父元素。
  2. background/border 为装饰,block 和 float 都属于布局,inline/inline-blocks 为内容,内容最上,布局次之,装饰最后的排序是合理的。
  3. 难于理解的是 “z-index:auto 或者看成 z-index:0 ,还有不依赖 z-index 的层叠上下文” ,这一堆是什么鬼。其实是这样的:
    • z-index:auto 与 z-index:0 在层叠顺序的排序中权重的一样的,如果遇上了,那么后面的元素出现在前面元素的上面,另外,定位元素的 z-index 默认为 auto。
    • 这里之所以又出现层叠上下文,是因为层叠上下文里可以嵌套层叠上下文,事实上页面根元素自带重叠上下文。内部的层叠上下文同样要参与父重叠上下文的排序,如果内部层叠上下文有 z-index 值,那么就按 z-index 的值去跟其他元素比较排序;但是如果内部层叠上下文不支持 z-index 值,那么就按照 z-index:0 来算。

2 层叠上下文的创建

CSS 2.1 标准

  1. 页面根元素天生带有层叠上下文
  2. z-index 值为数值的定位元素

CSS 3

  1. z-index 值为数值的 flex 子元素,即其父元素是 display:flex| inline-flex
  2. opacity 属性值小于 1 的元素
  3. transform 不为 none 的元素
  4. mix-blend-mode 属性值不为 “normal”的元素
  5. filter 值不为 none 的元素
  6. perspective 值不为 none 的元素
  7. isolation 值为 isolate 的元素
  8. position: fixed (目前浏览器实现不用,有的会有的不会)

CSS 3 中创建的层叠上下文大都不会设置 z-index 属性,所以它们跟其他元素排序时按照 z-index:0 来算。即使层叠上下文的创建规则改变了,但是它们仍然遵循之前的层叠顺序的排序规则。

三、总结

  1. 块级格式化上下文和层叠上下文都是概念,一个可以理解成箱子,一个可以理解成 z 轴
  2. 元素在一定的条件下创建块级格式化上下文/层叠上下文
  3. 拥有块级格式化上下文/层叠上下文的元素的内部元素遵循一定的规则来布局或层叠
  4. 拥有块级格式化上下文/层叠上下文的元素的内部可以嵌套块级格式化上下文/层叠上下文

本文参考
【1】深入理解BFC
【2】Visual formatting model
【3】BFC 神奇背后的原理
【4】什么是BFC
【5】The stacking context
【6】深入理解CSS中的层叠上下文和层叠顺序