CSS——关于z-index及层叠上下文(stacking context)

时间:2023-01-09 17:29:17

以下内容根据CSS规范翻译。

z-index

'z-index'
Value: auto | <integer> | inherit
Initial: auto
Applies to: positioned elements
Inherited: no
Percentages: N/A
Media: visual
Computed value: as specified

注意:z-index属性只能应用于position属性不为static的元素,即定位元素。

对于定位元素来说,z-index属性指定:

  1. 在当前层叠上下文中该定位元素盒子的堆叠层级(stack level);
  2. 该定位元素盒子是否形成新的层叠上下文(stacking context)。

z-index属性可取值的含义:
<integer>
如果值为整数,那么这个整数指的是这个盒子(the generated box)在当前层叠上下文中的堆叠层级。同时,这个盒子对内也形成一个层叠上下文。

auto
如果值为auto,那么这个盒子(the generated box)在当前层叠上下文中的堆叠层级为0。同时,这个盒子对内不形成层叠上下文,除非他是根元素。

注意:z-index值为 0 与z-index值为auto的区别。区别是前者对内创建一个新的层叠上下文,后者不创建新的层叠上下文。除了这点区别,两者的层级是一样的。
In this section, the expression "in front of" means closer to the user as the user faces the screen.

在CSS 2.1中,每一个盒子处在三维空间中。除了水平轴和垂直轴外,还有一个z轴(z-axis),这三个轴共同决定了盒子在三维空间中的位置。
盒子在z轴上的位置与盒子之间的重叠关系尤其相关。
下面讨论盒子如何在z轴上定位。

每一个盒子都处在层叠上下文中。如果在给定的层叠上下文中的给每一个定位的盒子(positioned box)指定一个整数的堆叠层级,那么这个堆叠层级就是该定位盒子相对于同一层叠上下文中其他定位盒子在z轴上的位置。拥有较大堆叠层级的盒子处在拥有较小堆叠层级的盒子的前面,即距离用户更近。堆叠层级可以为负值。在同一层叠上下文中,拥有相同堆叠层级的盒子根据后来居上的规则堆叠。


产生层叠上下文的情况(不全面):

  1. 根元素形成根层叠上下文(the root stacking context)。
  2. 任何定位元素(position不为static的元素)且该元素的z-index属性不为auto。

随着CSS的不断发展,也会有新的CSS属性会引入层叠上下文,比如CSS 3中的opacity属性等。
层叠上下文与包含块不存在绝对的相关关系。

在每一个层叠上下文中,以下层次按照后来居上的规则绘制(序号越大,堆放得越靠前,距离用户越近):
Within each stacking context, the following layers are painted in back-to-front order:

  1. 元素的background和borders
  2. 拥有负堆叠层级(negative stack levels)的子层叠上下文(child stacking contexts)
  3. 在文档流中的(in-flow),非行内级的(non-inline-level),非定位(non-positioned)的后代元素
  4. 非定位的浮动元素
  5. 在文档流中的(in-flow),行内级的(inline-level),非定位(non-positioned)的后代元素,包括行内块级元素(inline blocks)和行内表格元素(inline tables)
  6. 堆叠层级为 0 的子堆叠上下文(child stacking contexts)和堆叠层级为 0 的定位的后代元素
  7. 堆叠层级为正的子堆叠上下文

Within each stacking context, positioned elements with stack level 0 (in layer 6), non-positioned floats (layer 4), inline blocks (layer 5), and inline tables (layer 5), are painted as if those elements themselves generated new stacking contexts, except that their positioned descendants and any would-be child stacking contexts take part in the current stacking context.

上述关于层次的绘制规则递归地适用于任何层叠上下文。

层叠上下文

MDN中列举的创建新的层叠上下文的情形

文档中的层叠上下文由满足以下任意一个条件的元素形成:

  1. 根元素 (HTML),
  2. z-index 值不为 "auto"的 绝对/相对定位,
  3. 一个 z-index 值不为 "auto"的 flex 项目 (flex item),即:父元素 display: flex|inline-flex,
  4. opacity 属性值小于 1 的元素(参考 the specification for opacity),
  5. transform 属性值不为 "none"的元素,
  6. mix-blend-mode 属性值不为 "normal"的元素,
  7. filter值不为“none”的元素,
  8. perspective值不为“none”的元素,
  9. isolation 属性被设置为 "isolate"的元素,
  10. position: fixed
  11. 在 will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值(参考 这篇文章)
  12. -webkit-overflow-scrolling 属性被设置 "touch"的元素

在层叠上下文中,其子元素同样也按照上面解释的规则进行层叠。 特别值得一提的是,其子元素的 z-index 值只在父级层叠上下文中有意义。子级层叠上下文被自动视为父级层叠上下文的一个独立单元。

总结:

给一个 HTML 元素定位和 z-index 赋值创建一个层叠上下文,(opacity 值不为 1 的也是相同)。
层叠上下文可以包含在其他层叠上下文中,并且一起创建一个有层级的层叠上下文。
每个层叠上下文完全独立于它的兄弟元素:当处理层叠时只考虑子元素。
每个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会 在父层叠上下文中 按顺序进行层叠。
Note: 层叠上下文的层级是 HTML 元素层级的一个层级,因为只有某些元素才会创建层叠上下文。可以这样说,没有创建自己的层叠上下文的元素 将被父层叠上下文包含。

层叠上下文的层叠规则

先明确两个定义
树顺序 Tree Order

计算完元素自身的周边属性(背景颜色、背景图片、边框等)后,对渲染树进行前序深度优先遍历。
Preorder depth-first traversal of the rendering tree, in logical (not visual) order for bidirectional(双向) content, after taking into account properties that move boxes around.

元素 Element
在以下描述中,“元素”指的是实实在在的元素、伪元素以及匿名盒。伪元素和匿名盒被作为子节点(descendants)处理。
In this description, "element" refers to actual elements, pseudo-elements, and anonymous boxes. Pseudo-elements and anonymous boxes are treated as descendants in the appropriate places. For example, an outside list marker comes before an adjoining ':before' box in the line box, which comes before the content of the box, and so forth.

渲染顺序
堆叠层次的最底层距离用户最远,最上层距离用户最近,如图:
CSS——关于z-index及层叠上下文(stacking context)

层叠上下文(也可以叫堆栈上下文)的背景和最小负数定位(z-index为负)的子层叠上下文在层叠顺序(stack)的底层,最大的正数定位的子层叠上下文在层叠顺序(stack)的顶层。


默认情况下,如果一个画布(canvas)被包含在另一个画布中,那么它将是透明的;如果它没有被包含在另一个画布中,它将被用户代理指定一个默认颜色。根元素包含在画布中,这个画布可以向外无限扩展。最初,视口(viewport)的左上角被绑定在画布的原点。

形成层叠上下文的元素的子节点(descendants)的渲染顺序(参考z-index属性):
(按照if语句嵌套的方式理解下列规则)
注意:the element是创建父级层叠上下文的元素。
从渲染树的顶层开始遍历:
1、如果the element是根元素:

  1. 元素的背景颜色覆盖整个画布
  2. 元素的背景图片覆盖整个画布,起点在画布的原点。

2、如果the element是块级元素、列表项(list-item)或其他的与块级元素同级的元素(根元素除外):

  1. 背景颜色
  2. 背景图片
  3. 边框

     否则,如果the element是块级的表格元素:

  1. 表格得背景颜色,然后背景图片
  2. 列组(column group)的背景颜色,然后背景图片
  3. 列(column)的背景颜色,然后背景图片
  4. 行组(row group)的背景颜色,然后背景图片
  5. 行(row)的背景颜色,然后背景图片
  6. 表单元格(cell)的背景颜色,然后背景图片
  7. 所有的表格边框(in tree order for separated borders)

3、拥有负的z-index值的定位的子节点形成的层叠上下文,负数最小的最先渲染,然后根据树tree order渲染。

4、对于树顺序中的在文档流(in-flow),非定位,块级(block-level)子节点,如果该子节点是块级元素、列表项(list-item)或其他的与块级元素同级的元素:

  1. 背景颜色
  2. 背景图片
  3. 边框

     否则,如果该子节点是表格元素:

  1. 表格得背景颜色,然后背景图片
  2. 列组(column group)的背景颜色,然后背景图片
  3. 列(column)的背景颜色,然后背景图片
  4. 行组(row group)的背景颜色,然后背景图片
  5. 行(row)的背景颜色,然后背景图片
  6. 表单元格(cell)的背景颜色,然后背景图片
  7. 所有的表格边框(in tree order for separated borders)

5、对于树顺序中所有非定位的浮动子节点,渲染这些浮动子节点时,当做他们好像也形成了一个新的层叠上下文。但是,如果是定位的子节点和确实创建了一个新的层叠上下文的子节点,那么这两种子节点还是处在父级的层叠上下文中,而不是他们创建的那个。

第6条和第7条实在翻译不下去了,还是看原文吧。

 

6、If the element is an inline element that generates a stacking context, then:

    1. For each line box that the element is in:

      1. Jump to 7.2.1 for the box(es) of the element in that line box (in tree order).

 

7、Otherwise: first for the element, then for all its in-flow, non-positioned, block-level descendants in tree order:

    1. If the element is a block-level replaced element, then: the replaced content, atomically.

    2. Otherwise, for each line box of that element:

      1. For each box that is a child of that element, in that line box, in tree order:

        1. background color of element.

        2. background image of element.

        3. border of element.

        4. For inline elements:

          1. For all the element's in-flow, non-positioned, inline-level children that are in this line box, and all runs of text inside the element that is on this line box, in tree order:

            1. If this is a run of text, then:

              1. any underlining affecting the text of the element, in tree order of the elements applying the underlining (such that the deepest element's underlining, if any, is painted topmost and the root element's underlining, if any, is drawn bottommost).
              2. any overlining affecting the text of the element, in tree order of the elements applying the overlining (such that the deepest element's overlining, if any, is painted topmost and the root element's overlining, if any, is drawn bottommost).
              3. the text.
              4. any line-through affecting the text of the element, in tree order of the elements applying the line-through (such that the deepest element's line-through, if any, is painted topmost and the root element's line-through, if any, is drawn bottommost).
            2. Otherwise, jump to 7.2.1 for that element.

          For inline-block and inline-table elements:

          1. For each one of these, treat the element as if it created a new stacking context, but any positioned descendants and descendants which actually create a new stacking context should be considered part of the parent stacking context, not this new one.

          For inline-level replaced elements:

          1. the replaced content, atomically.

        Some of the boxes may have been generated by line splitting or the Unicode bidirectional algorithm.

      2. Optionally, the outline of the element (see 10 below).

    3. Optionally, if the element is block-level, the outline of the element (see 10 below).


8、All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order. For those with 'z-index: auto', treat the element as if it created a new stacking context, but any positioned descendants and descendants which actually create a new stacking context should be considered part of the parent stacking context, not this new one. For those with 'z-index: 0', treat the stacking context generated atomically.

9、Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order (smallest first) then tree order.

10、Finally, implementations that do not draw outlines in steps above must draw outlines from this stacking context at this stage. (It is recommended to draw outlines in this step and not in the steps above.)

 

参考资料:

1、z-index

2、Elaborate description of Stacking Contexts

3、The stacking context

下面看例子(没有考虑opacity),例子以根元素创建的层叠上下文为基础。

例子一

CSS代码:

div {
font
: 12px Arial;
}
body
{
border
:1px solid white;
}
span.bold
{ font-weight: bold; }
.text-style
{
text-align
: center;
border
:1px solid black;
}
#div1
{
float
: left;
width
: 300px;
height
: 50px;
background-color
: pink;
text-align
: right;
}
#div2
{
position
: absolute;
width
: 280px;
height
:120px;
top
: 50px;
background-color
: gray;
text-align
: right;
}

#div3
{
width
: 450px;
height
: 300px;
text-align
: right;
margin-top
: 20px;
}
#div4
{
position
: relative;
width
: 160px;
height
: 80px;
z-index
: 1;
top
:-300px;
background-color
: rgb(34,200,150);
}

 

HTML代码:

 1 <div id="div1" class="text-style">
2 <p><span class="bold">DIV #1</span>
3 <br />float left</p>
4 </div>
5
6 <div id="div2" class="text-style">
7 <p><span class="bold">DIV #2</span>
8 <br />position: absolute</p>
9 </div>
10
11 <div id="div3" class="text-style">
12 <p><span class="bold">DIV #3</span>
13 <br />no positioning, no float
14 </p>
15 </div>
16
17
18 <div id="div4" class="text-style">
19 <p><span class="bold">DIV #4</span>
20
21 <br />position: relative
22 <br /> z-index: 1
23 </p>
24 </div>

效果:

CSS——关于z-index及层叠上下文(stacking context)

从图中可以看出,浮动元素,定位元素,常规元素,设置了z-index的元素,四种元素在同一个层叠上下文中的层叠次序一目了然。

例子二

 CSS代码:

 1 #div5{
2 position: absolute;
3 border: 1px solid black;
4 width:350px;
5 height: 300px;
6 z-index: 0;
7 background-color: #5555FF;
8 text-align: right;
9 }
10 #div6{
11 position: absolute;
12 border: 1px solid black;
13 width:250px;
14 height: 200px;
15 z-index: auto;
16 background-color: #FFBB66;
17 text-align: right;
18 }
19 #div7{
20 position: absolute;
21 border: 1px solid black;
22 width:450px;
23 height: 180px;
24 z-index: -1;
25 background-color: #66FF66;
26 text-align: right;
27 }
28 #div8{
29 position: relative;
30 border: 1px solid black;
31 width:150px;
32 height: 250px;
33 z-index: 1;
34 background-color: #E38EFF;
35 }

HTML代码:

 1 <div id="div5" class="text-style">
2 <p><span class="bold">DIV #5</span>
3 <br />position: absolute
4 <br /> z-index: 0
5 </p>
6 </div>
7
8 <div id="div6" class="text-style">
9 <p><span class="bold">DIV #6</span>
10 <br />position: absolute
11 <br /> z-index: auto
12 </p>
13 </div>
14
15 <div id="div7" class="text-style">
16 <p><span class="bold">DIV #7</span>
17 <br />position: absolute
18 <br /> z-index: -1
19 </p>
20 </div>
21
22 <div id="div8" class="text-style">
23 <p><span class="bold">DIV #8</span>
24 <br />position: relative
25 <br /> z-index: 1
26 </p>
27 </div>

效果:

CSS——关于z-index及层叠上下文(stacking context)