CSS层叠顺序知多少

2019-08-30
本文约1.9k字

引言

我们都知道CSS中的z-index属性可以用来改变定位元素的层叠顺序,但是如果仅仅希望通过不断增大z-index数值来达到提高层叠次序的目的就有可能陷入欲求不能的迷惑中,其实关于层叠顺序这背后的学问还真不少。

瞧一瞧这是为什么?

我们先来看一组案例:

table

上图所示的两组桌子它们的dom结构和定位方式都是一模一样的,但是左边那组1号桌的桌号却显示在了2号桌的上面。

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
<style>
.left,.right{
position: relative;
width: 400px;
height: 200px;
}
.table{
position: absolute;
top: 25px;
width: 200px;
height: 150px;
border: 3px solid #ddd;
border-radius: 10px;
text-align: center;
line-height: 150px;
color: #fff;
font-size: 20px;
}
.table1{
left: 25px;
background: #7f8bdf;
}
.table2{
left: 175px;
background: #f59891;
}
.table .number{
position: absolute;
top: 10px;
right: 10px;
width: 30px;
height: 30px;
line-height: 30px;
background: #717477;
border-radius: 50%;
z-index: 2;
}
.right .table{
z-index: 1;
}
</style>

<div class="left">

<div class="table table1">

<div class="number">1</div>
</div>
<div class="table table2">

<div class="number">2</div>
</div>
</div>
<div class="right">

<div class="table table1">

<div class="number">1</div>
</div>
<div class="table table2">

<div class="number">2</div>
</div>
</div>

通过样式代码我们可以看到仅仅是右边的桌子设置了z-index数值就造成了这种差异,这是为什么?我们首先欢迎今天的第一位嘉宾——层叠上下文登场。

层叠上下文

层叠上下文(stacking context),是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸,HTML元素依据其自身属性按照优先级顺序占用层叠上下文的空间。

可以这样理解:我们看到的网页就像是一个桌面,HTML元素就是桌面上的一张张纸,层叠上下文就像是一个信封可以容纳其它纸张甚至是又一个信封。

envelope

当纸张铺开的时候我们可以一眼看到所有,但是一旦他们堆叠在一起就要有一个谁在上谁在下的顺序。

paper

层叠水平

层叠水平(stacking level)决定了同一个层叠上下文中元素在z轴上的显示顺序,就像桌子上纸张的顺序、信封里信纸的顺序。

层叠顺序

规则

层叠水平只是一个概念,那么我们根据什么来判定元素的层叠水平,有一个著名的示图揭示了规则,这就是层叠顺序(stacking order)。在CSS3推出后,这个规则得到了进一步的补充。

stacking order

规则很清楚,无需进一步解释说明。

注意事项

注意了注意了!

  • 层叠水平只在同一个层叠上下文中有意义,同一层叠上下文中谁大谁在上面。我们继续用纸和信封来举例子,如果根据层叠顺序,一个信封的层叠水平在一张纸的上面,那么信封里的信纸也必然在那张纸的上面,即使信纸的层叠顺序非常低,没有办法,容器的级别高,子孙享福。信封里的信纸继续按照规则排序,谁大谁在上面

  • 当元素的层叠水平完全相同时,处于dom流后面的元素会覆盖在上面

  • 很多文章说元素只要创建了层叠上下文就比普通元素层叠水平高

真的是这样吗?我们验证一下就知道了:

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
<style>
.floor{
width: 300px;
height: 300px;
background: #aaabc9;
}
.round{
position: absolute;
top: 100px;
left: 100px;
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
border-radius: 50%;
background: #f59891;
color: #fff;
z-index: 1;
}
.right .round{
z-index: -1;
}
</style>
<div class="left">
<div class="floor">地板</div>
<div class="round">
<div class="number"></div>
</div>
</div>
<div class="right">
<div class="floor">地板</div>
<div class="round">
<div class="number"></div>
</div>
</div>

左右两边,地板都是一个普通的块级元素,桌子分别创建了z-index为1和-1的层叠上下文,可它们并没有全部显示在地板上面,右边桌子显然被地板盖住了,这也符合上面图中负z-indexblock元素下面的规则。

floor

因此严谨地说,除了负z-index层叠上下文元素,其余层叠上下文都比普通元素层叠水平高。说了这么多,到底怎么才算创建了层叠上下文元素呢,别急,我们接下来就详细说一说。

创建层叠上下文

目前一共有三种方式来创建层叠上下文

  • 根元素 (HTML)自己就是一个层叠上下文元素
  • z-index为数值的定位元素
  • CSS3的属性(7层层叠顺序图中补充的不依赖z-index层叠上下文)

关于定位元素创建层叠上下文的方式,如果仅仅将position设置成了absoluterelative而没有设置z-index则该元素的z-index值为auto,仍然是一个普通元素,只是层叠水平高些,不会创建层叠上下文,只有设置了具体数值哪怕是0才行。至于fixed比较特殊,在现代浏览器即便没有设置z-index值也会自动创建层叠上下文。同一层叠上下文中,z-index值越大,层叠水平越高。

可以创建层叠上下文CSS3属性如下:

  • opacity 属性值小于 1 的元素
  • transform 属性值不为 “none”的元素
  • mix-blend-mode属性值不为 “normal”的元素
  • filter值不为“none”的元素
  • perspective值不为“none”的元素
  • isolation 属性被设置为 “isolate”的元素
  • will-change中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  • -webkit-overflow-scrolling属性被设置 “touch”的元素

非同辈元素层叠顺序判断

比较两个非同辈元素的层叠顺序时,要先向上找父元素或者祖先元素,如果两个元素互为兄弟节点的祖先元素中有一个创建了层叠上下文则这个元素在上,若两个祖先元素都创建了层叠上下文则按7层层叠顺序dom流顺序判断。如果互为兄弟节点的祖先元素都没有创建层叠上下文则所有祖先元素中最先创建非负z-index层叠上下文的元素在上。

总结

回归开头的例子,由于左边的桌子只是设置了绝对定位,并没有创建层叠上下文当父元素没有创建层叠上下文时,其连同子元素处于同一层叠上下文中,按照7层层叠顺序决定层叠水平。由于子元素z-index值大于0,因此两个桌码的层叠水平最高。右边桌子都设置了z-index数值,各自创建了一个层叠上下文层叠水平相同,后来者居上,层叠上下文元素的层叠水平会影响子元素,所以2号桌整个桌子都会覆盖在1号桌的上面。


层叠上下文概念摘自MDN web docs,部分配图来自Unsplash