我对 z-index 的理解至今仅停留在 定义一个元素在屏幕Z轴上的堆叠顺序,然而这是比较片面的一种理解

  1. z-index 并不是在任意地方都有效。它仅在 定义了 position 属性且值不为 static 的元素上生效

  2. 判断元素在 Z轴 上的堆叠顺序,不仅要比较 z-index 的大小,还会由元素的 层叠上下文层叠等级 共同决定

所以要理解 z-index 需要理解下面几个概念

  • 层叠上下文

  • 层叠等级

  • 层叠顺序

层叠上下文

在 CSS2.1 规范中,每个盒模型的位置是由三个维度决定的,分别是画布上的 X轴Y轴 以及表示层叠的 Z轴

正常情况下我们只能观察到元素在 X轴Y轴 上的分布,却看不出它们在 Z轴 上的层叠关系;但如果元素发生了堆叠,此时就能发现某些元素之间会出现相互覆盖的情况

如果一个元素产生了层叠上下文,那么它就是 层叠上下文元素,没有的我们暂称为普通元素

HTML 的根元素 <html></html> 存在一个 根层叠上下文

普通元素其实就在根层叠上下文中,但大家的上下文都一样,可以理解成大家都没有上下文

符合以下条件的元素,会产生层叠上下文,其自身变为层叠上下文元素

  1. HTML 的根元素 <html></html> 本身就具有层叠上下文,为根层叠上下文

  2. 普通元素设置 position 为非 static 的值且设置 z-index 为具体数值

  3. 使用了 CSS3 中部分新属性

层叠等级、层叠顺序

层叠等级由层叠顺序和 z-index 联合得出,层叠顺序是一种规定,如下图

层叠顺序

CSS3 属性的影响

元素属性满足以上条件之一,就会产生层叠上下文:

  1. 父元素的 display 属性值为 flex|inline-flex,子元素 z-index 属性值不为 auto 的时候,子元素为层叠上下文元素,会产生层叠上下文

  2. 元素的 opacity 属性值不是 1

  3. 元素的 transform 属性值不是 none

  4. 元素 mix-blend-mode 属性值不是 normal

  5. 元素的 filter 属性值不是 none

  6. 元素的 isolation 属性值是 isolate

  7. will-change 指定的属性值为上面任意一个

  8. 元素的 -webkit-overflow-scrolling 属性值设置为 touch

决定层叠的次序

先找到元素最近的 层叠上下文(普通元素的层叠上下文相同,均为根层叠上下文

如果

  • 元素在同一个层叠上下文中,那么直接根据元素自身 z-index层叠顺序 得出层叠等级,从而决定顺序

否则

  • 比较元素所在的 层叠上下文元素z-index

    元素所在层叠上下文元素的 z-index 较大者的元素层叠等级更高

    z-index 相同,则在文档中顺序靠后的层叠等级更高

例一

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
<style>
.box,
.child {
position: relative;
width: 100px;
height: 100px;
}
.a {
z-index: 3;
background-color: red;
}
.b {
z-index: 2;
background-color: gray;
top: -80px;
right: -40px;
}
.c {
z-index: 1;
background-color: blueviolet;
right: -20px;
top: -60px;
}
</style>
<body>
<div class="box">
<div class="child a"></div>
<div class="child b"></div>
</div>
<div class="box">
<div class="child c"></div>
</div>
</body>

其结果为

例一结果

分析:

  1. 寻找 .a .b .c 三元素的层叠上下文

  2. .boxposition: relative;,但没有 z-index,所以三个元素都是普通元素,都在 根层叠上下文

  3. 三者都是 block 且没有浮动

  4. 直接比较 z-index 得出顺序 .a > .b > .c

例二

在例一基础上给两个 .box 加上 z-index

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
<style>
.box,
.child {
position: relative;
width: 100px;
height: 100px;
}
.b1 {
z-index: 1;
}
.b2 {
z-index: 2;
}
.a {
z-index: 3;
background-color: red;
}
.b {
z-index: 2;
background-color: gray;
top: -80px;
right: -40px;
}
.c {
z-index: 1;
background-color: blueviolet;
right: -20px;
top: -60px;
}
</style>
<body>
<div class="box b1">
<div class="child a"></div>
<div class="child b"></div>
</div>
<div class="box b2">
<div class="child c"></div>
</div>
</body>

结果:

例二结果

分析:

  1. 寻找 .a .b .c 三元素的层叠上下文

  2. .a .b,.c 分别在 .b1,.b2 的层叠上下文中

  3. 根据 z-index 判断 .b2.b1 之上,.a.b 之上

  4. 得出 .c > .a > .b

例三

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
<style>
.box {
width: 200px;
height: 200px;
}
.parent {
z-index: 1;
background-color: red;
width: 200px;
height: 100px;
}
.child {
z-index: -1;
position: relative;
background-color: gray;
width: 100px;
height: 200px;
}
</style>
<body>
<div class="box">
<div class="parent">
<div class="child"></div>
</div>
</div>
</body>

此时结果为:

例三结果一

分析:

  1. .parent,.child 的层叠上下文都是根层叠上下文

  2. 比较 z-index 可以直接得出 .parent > .child

我们给 .box 添加 display: flex;,此时结果发生了变化:

例三结果二

分析:

  1. .parent 因为父元素是 flex,其自身形成了层叠上下文,成为层叠上下文元素

  2. 此时因为 层叠顺序 中的规则,层叠上下文元素的子元素必定在其 background/border 上方,所以 .child > .parent