z轴的学习规则

html 元素沿着相对其用户的一条虚构的 z 轴排开,层叠上下文就是对这些 html 元素的三维构想。
pic.1708406510231

背景

在 z 轴上,存在层叠上下文,他们有着遮挡关系的先后顺序,我们可以称之为层叠顺序,学习层叠上下文主要就是学习层叠顺序。在学习前,我们先来了解背景,我们试想一下,页面上为什么需要有 z 轴的存在?

举个例子:
常见的“顶部固定不动,内容区域向上滚动”的效果,我们给顶部设置position: fixed,内容区域设置overflow: auto。假设我们的内容在第一层,那顶部就在第一层之上,它不随第一层的滚动而滚动,因为 z 轴上的每一层都是相互独立的,这样轻而易举的达到了我们想要的效果。

如果不存在 z 轴,他们处在同一层,有可能实现这种效果吗?答案是有。内容向上滚动的同时,滚了多少距离,我们的顶部就向下滚动多少距离,这也可以实现相同的效果。但是这个过程的计算过程巨复杂,它使不变的元素(顶部)也进行了重新的计算,那么 GUI 渲染线程会重新计算 render tree,然后重新绘制页面元素。这将导致我们的页面性能非常差。

再举个常见的例子,比如视差、canvas,也是同个道理,都是为了更少的计算,让不变的保持不变。而这背后的原理,就是为了迎合浏览器的规则,减少回流。

所以答案就是:z 轴的存在,其实是网页性能优化的一种手段。使不变的元素保持不变,减少回流。

层叠上下文

我们可以把它们看做成一个个独立的空间,它们在 z 轴上存在顺序上的层叠关系。
层叠上下文存在以下特性

  • 层叠上下文可以包含在其他层叠上下文当中,形成一个嵌套层级
  • 每个层叠上下文完全独立于其他兄弟元素

层叠等级

层叠等级,也可以叫层叠级别/层叠水平,在同一个层级上下文中,等级越高,越靠近用户。

  • 普通元素的层叠等级由所处层叠上下文决定
  • 在同一个层叠上下文中,等级越高,越靠近用户
  • 不在同一个上下文中的层叠等级比较是没有意义的

用代码验证一下第三点:

1
2
3
4
5
<div class="yellow">
1
<div class="blue">999</div>
</div>
<div class="red">2</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.yellow {
background: yellow;
height: 100px;
position: relative;
z-index: 1;
}
.blue {
background: blue;
height: 100px;
position: absolute;
z-index: 999;
}
.red {
background: red;
width: 50px;
height: 50px;
position: relative;
left: 20px;
top: -20px;
z-index: 2;
}

pic.1708406521813

.yellow的层叠等级为 1,.red的层叠等级为 2,.blue为 999,.yellow.red在同一个根层叠上下文下。
但是由图可见,.blue.red下面,这是因为.yellow的层叠等级比.red小,也证明了不在同一个上下文中的比较是没有意义的。

创建层叠上下文

  • html为根层叠上下文
  • position属性为absoluterelative并设置了z-index属性为具体数值
  • position值为fixedsticky的元素
  • css3 的一些属性(文章后面介绍)

层叠顺序

层叠规则,表示元素在发生层叠时,有着特定的层叠顺序。层叠上下文和层叠等级是概念,而层叠顺序是规则。
层叠上下文的 background/border < z-index 负值 < block 元素 < float 元素 < inline-block/inline < z-index: 0 / z-index: auto < z-index: 正值
层叠顺序图.png

DOM 顺序和层叠顺序

从图中可以看出,层叠低的在层级高的下面,就像往容器里面放东西一样。它其实迎合栈的层叠规则,先进后出。所以浏览器会优先渲染层叠等级低的元素,再渲染比它高的。

所以结合实际的应用,在编码时,我们应该自然而然的把层叠等级低的写在最前面,让渲染更流畅。

1
2
3
<div style="z-index: -100">div1</div>
<div style="z-index: 0">div2</div>
<div style="z-index: 1">div3</div>

如果层级高的在下面会怎样?看个例子
我们先往容器(html)里放了一个 div1,在放 div2,div3,假设 3 个 div 都是层叠元素,div1 层叠等级最高,并且在最下面

1
2
3
<div style="z-index: 1">div1</div>
<div style="z-index: 0">div2</div>
<div style="z-index: -100">div3</div>

以上代码,假设先渲染等级高的(div1),那渲染等级低的元素时,是不是还要把它上面的元素先拿出来。

万能公式

  • 同一个层叠上下文,谁的层叠等级大,谁再上面。
  • 不在同一个层叠上下文,找到它们的父级为兄弟节点,再比较。
  • 当层叠等级和层叠顺序相同时,后来居上。

下面我们来验证一下层叠顺序和万能公式是否准确。
例子 1:z-index: 负值 < block 元素:

1
2
3
4
5
6
<div class="box">
<div class="parent">
parent
<div class="child">child</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.box {
}
.parent {
width: 200px;
height: 100px;
background: #168bf5;
}
.child {
width: 100px;
height: 200px;
background: #32d19c;
position: relative;
z-index: -1;
}

pic.1708406544047

.parent 是个普通的块元素,.child 是带负值的 z-index 层叠上下文元素,.parent 在.child 上面,所以 z-index: 负值 < block 元素成立。

例子 2: 层叠上下文的 background/border < z-index 负值
继续上个例子,我们给.parent 添加 position: relative 和 z-index: 0,其余代码不变

1
2
3
4
5
6
<div class="box">
<div class="parent">
parent
<div class="child">child</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.box {
}
.parent {
width: 200px;
height: 100px;
background: #168bf5;
position: relative;
z-index: 0;
}
.child {
width: 100px;
height: 200px;
background: #32d19c;
position: relative;
z-index: -1;
}

pic.1708406555021

这时候.parent 变成了带 background 的层叠上下文元素,.child 依然是带负值的 z-index 层叠上下文元素。网上的很多文章拿这个例子来验证 background/border < z-index 负值,其实是不正确的。.child 在.parent 上,是因为.child 嵌套在.parent 里面,所以.child 在上面。也就是说他们不在用一个层叠上下文之下,所以他们的比较是没有意义的。

关于 background/border < z-index 负值,我还没想到如何验证。

例子 3:block 元素 < float 元素 < inline-block/inline

1
2
3
4
<div class="box">
<img src="../../images/js1.jpg" alt="" />
<span>我是重要的内容,我的等级很高的哦</span>
</div>
1
2
3
4
5
6
7
8
9
10
11
.box {
background: red;
height: 300px;
}
img {
float: left;
}
span {
position: relative;
left: -30px;
}

pic.1708406566018

.box 是块元素,img 是浮动元素,span 是 inline 元素,inline 在最上面,img 在中间,所以block 元素 < float 元素 < inline-block/inline成立。

例子 4:同一个层叠上下文,谁的层叠等级大

1
2
3
4
5
6
7
8
9
10
<div class="box">
<div class="box1">
<!-- 盲僧 -->
<img src="../../images/ms.jpg" alt="" />
</div>
<div class="box2">
<!-- 剑圣 -->
<img src="../../images/js1.jpg" alt="" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.box1 {
position: relative;
z-index: auto;
}
.box1 img {
position: absolute;
z-index: 2;
}
.box2 {
position: relative;
z-index: auto;
}
.box2 img {
position: relative;
z-index: 1;
}

pic.1708406574167

.box1 和 .box2 的 z-index 值为 auto,此时不是层叠上下文元素,两张图片都属于在根层叠上下文下,因为盲僧在剑圣上面,所以同一个层叠上下文,谁的层叠等级大成立

例子 5:层叠等级和层叠顺序相同时,后来居上
在例子 4 的基础上,我们把.box1 和.box2 的 z-index: auto 改为 z-index: 0

1
2
3
4
5
6
7
8
9
10
<div class="box">
<div class="box1">
<!-- 盲僧 -->
<img src="../../images/ms.jpg" alt="" />
</div>
<div class="box2">
<!-- 剑圣 -->
<img src="../../images/js1.jpg" alt="" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.box1 {
position: relative;
z-index: 0;
}
.box1 img {
position: absolute;
z-index: 2;
}
.box2 {
position: relative;
z-index: 0;
}
.box2 img {
position: relative;
z-index: 1;
}

pic.1708406589649

此时.box1 和.box2 都变成了层叠等级为 0 的层叠上下文元素,并且在同个层叠上下文下。剑圣在盲僧上面,所以层叠等级和层叠顺序相同时,后来居上成立。

css3 中的属性对层叠上下文的影响

介绍几个比较常用的,如下:

  1. 父元素的 display 属性值为 flex | inline-flex,子元素的 z-index 属性值不为 auto,子元素为层叠上下文元素
  2. 元素的 opacity 值不为 1
  3. 元素的 filter 属性值不是 none
  4. 元素的 transform 属性值不是 none

例子 6:父元素 display: flex | inline-flex 与层叠上下文

1
2
3
4
5
6
<div class="box">
<div class="parent">
parent
<div class="child">child</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.box {
}
.parent {
width: 200px;
height: 100px;
background: #168bf5;
/* 虽然设置了z-index,但是没有设置position,z-index无效,.parent还是普通元素,没有产生层叠上下文 */
z-index: 1;
}
.child {
width: 100px;
height: 200px;
background: #32d19c;
position: relative;
z-index: -1;
}

pic.1708406603779
继续例子 6,我们把.parent 变成层叠上下文元素,
例子 7:层叠上下文的 background/border < z-index 负值

1
2
3
4
5
6
<div class="box">
<div class="parent">
parent
<div class="child">child</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.box {
display: flex;
}
.parent {
width: 200px;
height: 100px;
background: #168bf5;
/* .box设置了display: flex; 这时候.parent变成了层叠上下文 */
z-index: 1;
}
.child {
width: 100px;
height: 200px;
background: #32d19c;
position: relative;
z-index: -1;
}

pic.1708406621323

.child 跑到了上面,所以层叠上下文的 background/border < z-index 负值成立。

例子 8:元素的 opacity 值不为 1

1
2
3
4
5
<div class="box">
<div>
<img src="../../images/js1.jpg" alt="" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
.box {
}
.box div {
width: 300px;
background: #168bf5;
}
div img {
width: 200px;
height: 200px;
position: relative;
z-index: -1;
}

pic.1708406632213
因为 z-index: 负值 < 块元素,所以图片在蓝色背景下。然后我们给 div 添加 opacity: 0.5,其余不变

1
2
3
4
5
<div class="box">
<div>
<img src="../../images/js1.jpg" alt="" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
.box {
}
.box div {
width: 300px;
background: #168bf5;
opacity: 0.5;
}
div img {
width: 200px;
height: 200px;
position: relative;
z-index: -1;
}

pic.1708406639748

添加 opacity 后,div 变成了层叠上下文元素,图片跑到了上面,所以层叠上下文的 background/border < z-index 负值成立。

例子 9:元素的 filter 属性值不是 none
在例子 8 上,我们给 div 添加 filter: blur(5px),其余不变

1
2
3
4
5
<div class="box">
<div>
<img src="../../images/js1.jpg" alt="" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
.box {
}
.box div {
width: 300px;
background: #168bf5;
filter: blur(5px);
}
div img {
width: 200px;
height: 200px;
position: relative;
z-index: -1;
}

pic.1708406650744

同理,结论成立。

例子 10:元素的 transform 属性值不是 none
在例子 8 上,我们给 div 添加 transform: rotate(45deg),其余不变

1
2
3
4
5
<div class="box">
<div>
<img src="../../images/js1.jpg" alt="" />
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
.box {
}
.box div {
width: 300px;
background: #168bf5;
transform: rotate(45deg);
}
div img {
width: 200px;
height: 200px;
position: relative;
z-index: -1;
}

pic.1708406659567

同理,结论成立。

开发调试工具
可以用过 layers 来查看页面的层叠布局,检查遮挡关系
More tools -> Layers
pic.1708406666079

总结

  1. z 轴的存在,其实是网页性能优化的一种手段。使不变的元素保持不变,减少回流。
  2. 因为浏览器优先渲染层叠等级低的元素,所以编码时,我们应该自然而然的把层叠等级低的元素写在前面,让渲染更为流畅。
  3. 查看层叠等级时,先查看是否在同个层叠上下文下,如果是,层叠等级越高越在上面,层叠等级和层叠顺序相同时,后来居上。如果不是,那么判断所处层叠上下文的等级。
  4. 避免滥用 z-index。现在我们知道 inline > float,那么我们只需要把元素变成行内元素即可覆盖在浮动元素之上。过渡使用定位元素会使页面变复杂。
  5. 定位或者 css3 属性 display: flex | inline-flex 下,z-index: auto 所在的元素是普通元素,z-index: 0 是层叠元素。
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×