前言

在前端开发中你一定用过z-index,它用于让某些元素在视觉上更接近用户,但是有时你又会发现它没有用了,这是因为z-index只是css层叠规则中的一部分,它的起效需要其他css的作用,下面我们就来简单聊聊css层叠这部分的知识,让你不再只是会乱用z-index。

什么是层叠上下文(stacking context)

这里用一下MDN的定义


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

当然这么直接搬文档大部分人一开始肯定是看不懂的,我们可以这么理解,层叠上下文是一个有很多层的盒子,盒子里面有很多书(HTML元素),这些书会根据他们设置的CSS样式摆放在不同的层上,所以我们可以把层叠上下文理解成是一个分层的大容器(虽然这么理解不完全正确)

如何产生层叠上下文

这是MDN说明的可以产生层叠上下文的情况

然后我们翻译一下

  1. 文档的根元素(HTML元素)
  2. 设置z-index为非auto值的绝对定位和相对定位元素
  3. 设置了position为fixed或者sticky的元素
  4. 父元素设置了display:flex,同时自身设置了z-index为非auto值的元素
  5. opacity小于1的元素
  6. z-index非auto的网格布局(gird)子元素
  7. mix-blend-mode属性值不是normal的元素
  8. 下面这些属性值任意一个不是none的元素
    • transform
    • filter
    • perspective
    • clip-path
    • mask/mask-image/mask-border
  9. isolation为isolate值为的元素
  10. -webkit-overflow-scrolling值为touch的元素
  11. will-change为非初始值的元素
  12. contain属性为layout,paint,strict,content的元素

当然你不需要全部记下来,其实记住前六点应该差不多了

再来聊聊层叠等级(stacking level) 和 层叠顺序(stacking order)

你如果之前看过其他的文章,你就会发现这里我和他们对stacking level的翻译不大一样,其实我一直觉得层叠水平多少有点歧义和不够容易理解,所以我就在这里使用层叠等级这个翻译,其实老实说我并不想放入这两个名词,因为没有必要反而容易混淆,不过最后还是放上来了,因为考虑到有些朋友可能是看过其他博客才来的,怕没有这两个东西不习惯(划掉)

其实层叠等级和层叠顺序在官方文档中没有明确的定义(有可能有,但我没找到),他们只是文档中层叠上下文中用法中偶尔出现了几次的名词,所以我就用自己的理解来下定义了

层叠等级:每一个元素都在包含他的那个层叠上下文中有一个等级,这个等级级就叫做层叠等级,当元素发生重叠时,层叠等级越高,离用户越近,层叠等级小的会被盖住,你还需要注意以下的几点

1. 一个元素只能有一个层叠等级,因为它只能属于一个层叠上下文,就像一本书只能属于一个盒子
2. 一个元素的层叠等级只在它属于的层叠上下文中生效,就像一本书属于一个盒子,他不能跑出这个盒子单独排序

层叠顺序:层叠顺序是一种z轴上的排序规则,这个规则是:同一个层叠上下文中层叠等级高的优先级较高,即层叠等级越高,离用户越近

看看层叠等级有多少级

又到了我们熟悉的丢文档的时间,这是CSS规范2018有关层叠等级的说明

翻译一下:

在一个层叠上下文A中,分层如下(层叠等级逐渐提高):

  1. 层叠上下文A的背景和边框
  2. 在这个层叠上下文A中生成了层叠上下文B且z-index为负值的元素
  3. 常规流非定位块盒
  4. 非定位的浮动盒子
  5. 常规流非定位行盒(注意,这里的行盒包括display为inline的元素和inline-bloc的k元素)
  6. 这一条有三点
    • 生成了堆叠上下文且z-index为0的元素
    • z-index为auto的定位元素
    • 某些不需要设置z-index为数值就能生成堆叠上下文的元素(主要是CSS3新增的)
  7. z-index为正值的堆叠上下文B

在一个层叠上下文中,层叠等级越高的,离用户越近,如果层叠等级相同,源代码中后面的元素会覆盖前面的元素
概括起来就是谁大谁上,后来居上

看几个例子

例子一

估计看了这么多的理论知识,不少人是一脸懵逼的,我们在这里就综合一下,用几个例子来运用前面的知识

HTML

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
<body>

<div id="box1">
box1:我是z-index为负值的堆叠上下文
</div>
<div id="box2">
box2:我是普通块盒
</div>
<div id="box3">
box3:我设置了浮动
</div>
<div id="box4">
box4:我是行块盒
</div>
<div id="box5">
box5:我是定位元素,z-index为0,在蓝色盒子的前面
</div>
<div id="box6">
box6:我是定位元素,z-index为auto
</div>
<div id="box7">
box7:我是定位元素,z-index为0,在蓝色盒子的后面
</div>
<div id="box8">
box8:我是z-index为正值的堆叠上下文
</div>

</body>

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
div
{
width: 150px;
height: 150px;
box-sizing: border-box;
padding: 0 30px;
}

#box1
{
background-color: orange;
position: relative;
z-index: -1;
margin-top: 0px;
}

#box2
{
background-color: yellow;
margin-top: -60px;
}

#box3
{
background-color: green;
float: left;
margin-top: -80px;
}

#box4
{
background-color: #00bbff;
display: inline-block;
margin-top: 0px;
margin-left: -150px;
}

#box6
{
background-color: blue;
position: relative;
margin-top: 0px;
top: -80px;
z-index: auto;
}


#box5
{
position: absolute;
background-color: #4cae4c;
z-index: 0;
top: 320px;
left: 96px;

}

#box7 {
position: absolute;
background-color: pink;
z-index: 0;
top: 320px;
left: 328px;
}

#box8
{
background-color: purple;
position: relative;
z-index: 1;
margin-top: 0px;
top: -140px;
}

结果


在这个文档中,一共有5个元素产生了层叠上下文,分别是html元素,box1,box5,box7,box8,
html元素产生的堆叠上下文中有8个元素(box1 - box8),这8个box元素有他们对应的层叠等级(具体的等级参照上面),
层叠等级越高,离用户越近,层叠等级低的元素被盖住,然后层叠等级相同的(比如5,6,7),按照源文件中的先后顺序覆盖

例子二

现在我们加大难度,把html文件改一下,在box5和box7中分别加一个元素
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
<div id="box1">
box1:我是z-index为负值的堆叠上下文
</div>
<div id="box2">
box2:我是普通块盒
</div>
<div id="box3">
box3:我设置了浮动
</div>
<div id="box4">
box4:我是行块盒
</div>
<div id="box5">
box5:我是定位元素,z-index为0,在蓝色盒子的前面
<div style="position: absolute; z-index: 2; background-color: #8a6d3b; top: 97px; left: 0px">
我是在box5里面的box5-1,z-index为2,绝对定位
</div>
</div>
<div id="box6">
box6:我是定位元素,z-index为auto
</div>
<div id="box7">
box7:我是定位元素,z-index为0,在蓝色盒子的后面
<div style="position: absolute; z-index: -1; background-color: #FF5B17; top: 101px; left: -2px">
我是在box6里面的box7-1,z-index为-1,绝对定位
</div>
</div>
<div id="box8">
box8:我是z-index为正值的堆叠上下文
</div>

然后看起来匪夷所思的事情发生了

z-index比较大的元素居然在z-index较小的元素下面,这是为什么呢,我们来分析一波,首先最外面的层叠上下文依旧是html元素,
html层叠上下文中有box1 - box8一共8个元素,其中box5和box7因为设置了z-index为0和定位,box5和box7 分别形成了新的层叠上下文,
box5-1和box7-1分别在box5和box7形成的层叠上下文中,box5-1和box7-1设置的z-index只在他们所属的层叠上下文中生效,而他们两个根本不在一个层叠上下文中,所以z-index不能影响他们的覆盖关系,影响他们覆盖关系的是他们所属的层叠上下文,因为box5和box7在html形成的层叠上下文中是box7覆盖box5,所以box7层叠上下文和它层叠上下文中所有的元素都比box5层叠上下文中的元素里用户近,所以box7-1会覆盖box5-1

可以举个形象点的例子,层叠上下文相当于大箱子,没有形成层叠上下文的元素的相当于书,在html这个大箱子中,有box1,box5,box7,box8这几个箱子,box2,box3,box4,box6这几本书,他们离用户的顺序是(从远到近)box1,box2,box3,box4,box5,box6,box7,box8,而box5-1和box7-1是在box5和box7这两个箱子里面的,既然box7这个箱子本身就比box5高,所以box7中的书肯定也比box5中的书高,而box5-1和box7-1设置的z-index只是能让他们在box5和box7这两个箱子中和其他书排序时能排得更高而已

来做个小练习看看是不是掌握了

现在我们在box6中加个box6-1,设置position为absolute,z-index为1,那么box6-1和box8哪个在前面?

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
<body>

<div id="box1">
box1:我是z-index为负值的堆叠上下文
</div>
<div id="box2">
box2:我是普通块盒
</div>
<div id="box3">
box3:我设置了浮动
</div>
<div id="box4">
box4:我是行块盒
</div>
<div id="box5">
box5:我是定位元素,z-index为0,在蓝色盒子的前面
<div style="position: absolute;z-index: 2;background-color: #8a6d3b;top: 235px;left: 73px;">
我是在box5里面的box5-1,z-index为2,绝对定位
</div>
</div>
<div id="box6">
box6:我是定位元素,z-index为auto
<div style="position: absolute;z-index: 1;background-color: #d9534f;top: 90px;left: 127px;">
我是box6里的box6-1,z-index为1
</div>
</div>
<div id="box7">
box7:我是定位元素,z-index为0,在蓝色盒子的后面
<div style="position: absolute;z-index: -1;background-color: #FF5B17;top: 236px;left: -44px;">
我是在box6里面的box7-1,z-index为-1,绝对定位
</div>
</div>
<div id="box8">
box8:我是z-index为1的堆叠上下文
</div>

</body>

因为box6没有形成层叠上下文,所以box6-1也在html形成的层叠上下文中,box6-1和box8的z-index都是1,层叠等级相同,根据后来居上原则,box8会覆盖box6-1

后记

那么我要分享的到这里就结束了,如果我有写错的地方或者你有其他想法或者问题,欢迎在评论区留言

测试代码:链接: https://pan.baidu.com/s/1YQvBZ8AiOLQu3c9KLobz0A 提取码: sena