理解css2.1中的z-index

理解z-index堆叠上下文 stacking context

堆叠上下文

stacking context称为堆叠上下文。在css中,有很多上下文的概念,那么什么是上下文呢?上下文就是一个布局环境的意思,在同一个环境内,各个元素的布局方式,要遵循这个环境里面定义的各种规则。比如BFC,只要在同一个BFC里面,在达到条件的时候就可以触发特定的布局行为,比如浮动、比如外边距合并,当不在同一个BFC的时候,这些特定的布局行为就不生效了。

堆叠上下文BFC一样,也是一个布局环境,它用来解决什么问题呢?它解决的是当处于不同的布局上下文(BFC IFC flex table 等)中的box与其它box发生重叠的时候,它们之间的重叠先后顺序问题,就是“谁覆盖谁”的问题。 所以堆叠上下文是针对所有的box而言的,不管这些box的布局上下文是不是相同类型,是不是同一个,只要box发生重叠,它们就要满足堆叠上下文的规则进行展示。

堆叠上下文可以包含子堆叠上下文,就像BFC可以包含BFC一样。

在css2.1里面,每个box在渲染时的位置由3个值构成:x, y, z。 x和y分别是指水平和垂直方向的位置,它们是根据前面已经学过的box model float normal-flow position等相关布局内容计算出来的,描述box在网页这个平面中的布局坐标。除了x,y之外,box的布局位置还有一个z值,描述了box在用户的眼睛与网页平面的那条垂直线上的布局层次,box的z值越大,代表它的层级越高,离用户的眼睛就越近,层级高的box总是渲染在层级低的box的前面,当它们发生重叠时,层级高的就会覆盖在层级低的上面。那条垂直线,就是css布局里面的z轴,stacking context描述的就是box在z轴上的布局先后关系。

z值所对应的布局层级,就是box在z轴上的stack level。默认情况下,所有boxstack level都是0,在同一个stacking context当中,如果boxstack level相同,则box会按照它们在document tree中的先后关系堆叠渲染,也就是说发生重叠时,后创建的box会覆盖在先创建的box之上。比如:

1
2
3
4
<div style="width: 500px; margin: 10px auto">
<div style="height: 100px; background-color: cadetblue;"></div>
<div style="margin: -20px 0 0 20px; height: 100px; background-color:cornflowerblue"></div>
</div>

这个效果中,两个带背景色的div的stack level都是0,且位于同一个stacking context当中,但是第二个div因为负的margin导致它与前一个div发生重叠,最后按照元素的先后关系,第二个div覆盖在了第一个div之上。

一开始网页只有一个stacking context,由root element创建。
每个box只属于一个stacking context

z-index property

z-index这个属性有两个作用,一是指定box在它所在的堆叠上下文中的层级stack level,二是可以给box创建新的堆叠上下文,布局子内容。它的定义如下:

Value: auto | <integer> | inherit
初始值: auto
应用于: positioned elements,是指positionrelative | absolute | fixed的元素
是否可继承: no
百分比: 不支持

各个值的含义如下:

  • <interger> 整型值用来指定box在当前堆叠上下文中的stack level,可以为任意整数,包括负数;同时它还会创建一个新的堆叠上下文;
  • auto 指定box在当前堆叠上下文中的stack level0,但是不会创建新的堆叠上下文

注意:z-index:0z-index:auto的区别,两者都代表stack-level是0,但是z-index:0会创建新的堆叠上下文,而z-index:auto不会。

painting order

结合stack level以及box tree本身布局的特性,每个堆叠上下文都遵循以下这个顺序(painting order)在z轴上渲染相关的内容。这个顺序依次是从下到上,越靠后的内容它在堆叠上下文中的层级越高:

  1. 先渲染形成堆叠上下文boxbackgroundborder
  2. 渲染stack levelchild stacking contextsstack level越小的在越底下
  3. 渲染未定位的、文档流中的block box
  4. 渲染未定位的float box,注意float box可以结合position: relative变为定位的
  5. 渲染未定位的、文档流中的inline box,包括普通的inline content以及inline tablesinline blocks
  6. 渲染stack level0child stacking contexts,以及设置了定位的但stack level也为0后代元素
  7. 渲染stack level大于0child stacking contextsstack level越大的在越上面

注重说明:

  • 上面顺序中的每一项,都可以看作是一个层,越后面的层,在z轴的位置,肯定比前面的层高;在同一个层中,不同的内容首先根据stack level来决定堆叠顺序,stack level相同时,则根据它们在document tree中的先后顺序,决定堆叠顺序;
  • 未定位的意思是值positionstatic,因为其它position值,当设置了非autoz-index后,会创建stacking context
  • stack level0child stacking context是指,设置了非staticposition,且z-index:0box所新建的stacking context
  • stack level0定位元素是指,设置了非staticposition,且z-index:autobox
  • 上面说的是在一个stacking context中的堆叠渲染顺序,第2 6 7都涉及都有child stacking context,如果有,则每个child stacking context也都按照上面的顺序来堆叠展示自己内部的内容,所以stacking context的堆叠渲染,是一个类似递归的渲染处理方式。某个child stacking context内部,如果一个box的stack level设置的特别大,超过网页里所有其它内容的stack level,它最终也不一定会渲染在页面内容的最顶部,因为它是在一个child stacking context里面渲染的,只要这个child stacking context本身的stack level低于其它内容,它自己包括它的子内容就始终会在其它内容下面渲染。

这是css关于以上层叠渲染顺序的算法逻辑:堆叠上下文的详细算法逻辑,也是浏览器帮我们实现的。

通过下面这个例子来理解上面的层叠渲染顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>

<style type="text/css">
.layer1 {
position: relative;
z-index: 0;
width: 500px;
padding: 10px;
border: 1px solid #ccc;
background-color: #e2e2e2;
}

.layer2 {
position: absolute;
width: 100px;
height: 100px;
z-index: -1;
background-color: darkgoldenrod;
}

.layer3 {
margin: 1em;
padding: 10px;
border: 5px solid #ccc;
}

.layer4 {
width: 100px;
height: 200px;
float: left;
background-color: skyblue;
margin: 10px;
}

.layer5 {
margin-left: -57px;
background: red;
color: #fff;
display: inline-block;
}

.layer6 {
position: absolute;
width: 100px;
height: 100px;
background-color: cadetblue;
top: 190px;
}

.layer7 {
position: absolute;
width: 100px;
height: 100px;
background-color: blueviolet;
top: 231px;
left: 45px;
}
</style>
</head>

<body>
<div class="layer1">
<div class="layer2"></div>
<div class="layer3">Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui vero quae nostrum debitis
<span class="layer4"></span>necessitatibus commodi, soluta ea amet perferendis magni voluptate provident nam.
Dicta ducimus
rerum aspernatur id eaque assumenda.</div>
<div class="layer3">
<div class="layer5">Lorem ipsum dolor</div> sit amet consectetur adipisicing elit. Qui vero quae nostrum debitis
necessitatibus commodi, soluta ea amet perferendis magni voluptate provident nam. Dicta ducimus rerum
aspernatur id eaque assumenda.
</div>
<div class="layer6"></div>
<div class="layer7"></div>
</div>
</body>

</html>


这个例子中的css类全部都用层叠顺序中对应的顺序名,方便理解它们的层叠关系。

后记

上面的内容属于css2.1中的规范,到目前来说仍然属于比较靠谱的知识点,但是css发展地很快,后面新增的内容可能会调整这个堆叠上下文的知识点,所以上面的知识,在当前来说是不完整的。 不过以上内容,对于处理网页开发中涉及层叠渲染顺序的问题,已经够用了。