# 前言

在前端开发中你一定用过 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

<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

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 不变

<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 哪个在前面?

<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

更新于 阅读次数