深入理解CSS中的z-index

998 阅读7分钟

深入理解z-index

MDN上的定义是z-index 属性设定了一个定位元素及其后代元素或 flex 项目的 z-order。 当元素之间重叠的时候, z-index 较大的元素会覆盖较小的元素在上层进行显示。

但是这个说明太含糊了,当遇到z-index不生效的情况时,就不知所以然了,最近也查看了很多和z-index相关的资料,决定把z-index相关知识系统性的梳理一遍.

以前我总是很片面的认为元素在Z轴上的层叠顺序只跟z-index值的大小有关, 属性值大的元素显示在上面、属性值小的元素显示在下面,但是就像下面, 为啥z-index不生效呢,明明box1的z-index属性值大于box2的.

image-20210528103513860

事实上z-index属性并非对所有元素都生效, 它仅对定位元素生效而且定位元素的prosition属性值不为static时才会起作用:

image-20210529224536354

其实判断元素在Z轴的层叠顺序取决于两个方面: 元素所在的层叠上下文元素自身的层叠级别, 在此之前我们先了解一下这两个概念:

z-index

通常情况下,html页面可以被认为是二维的,因为文本,图像和其他元素被排列在页面上而不重叠。在这种情况下,只有一个渲染进程,所有元素都知道其他元素所占用的空间。

CSS 2.1 中, 所有的盒模型元素都处于三维坐标系中。 除了我们常用的横坐标和纵坐标, 盒模型元素还可以沿着“z 轴”层叠摆放, 当他们相互覆盖时,z轴顺序就变得十分重要。这意味着 CSS 允许你在现有的渲染引擎上层叠的摆放盒模型元素。 所有的层都可以用一个整数( z 轴顺序)来表明当前层在 z 轴的位置。 数字越大, 元素越接近观察者。Z 轴顺序用 CSS 的 z-index 属性来指定。z-index的属性值默认为auto,可设置值为一个整数、可为正整数也可以是负整数

image-20201009110850693

层叠上下文

MDN上的定义: 我们假定用户正面向(浏览器)视窗或网页,而 HTML 元素沿着其相对于用户的一条虚构的z轴排开,层叠上下文就是对这些HTML元素的一个三维构想。众HTML元素基于其元素属性按照优先级顺序占据这个空间。

那么如何才能创建层叠上下文呢?我之前在网上看到过一个总结, 很方便记忆: 目前有三类方法创建层叠上下文

  • 元素自身就能创建的
  • 需要结合z-index才能创建的
  • 不需要z-index 就能创建的

一、元素自身形成层叠上下文

​ 文档根元素(<html>)会自动形成一个层叠上下文, 不需要结合任何其他属性

二、需要配合z-index才能触发创建层叠上下文的

position值为 absolute(绝对定位)或 relative(相对定位)且 z-index 属性值不为 auto 的元素;

采用flex布局容器的子元素, 且子元素 z-index 属性值不为 auto 的元素;

三、不要配合z-index就能触发创建层叠上下文的

position值为 fixed(固定定位)或 sticky(粘滞定位)的元素;

透明度opacity属性值不为1的元素

转换transform属性值不为none的元素

滤镜filter属性值不为none的元素

上面列举出来的都是一些常用到的属性,当然还有其他的属性值设置也能触发元素形成层叠上下文,这里就不一一列举了,有兴趣的同学可以去MDN文档查看.这里我们需要注意的几点:

  • 层叠上下文可以包含在其他层叠上下文中, 由于根元素HTML本身就是一个层叠上下文,所以页面文档中的创建的层叠上下文都是HTML元素层级的一个子级
  • 当某个元素创建了层叠上下文后, 应当把它及其后代当成一个整体,去判断层叠顺序
  • 父子元素、兄弟元素都可能会处于同一层叠上下文中

层叠级别

在不考虑层叠上下文的情况下, 元素的层叠级别就是判断发生层叠时,元素在Z轴如何显示的依据, 下图就是著名的7阶层叠水平图:

v2-1ec9491a660c0e11b7272633976da869_1440w

根据上图,我们发现元素自身的层叠级别不仅仅是只取决于z-index属性值的大小;从下往上依次是background/border、负z-index元素、块级元素、浮动元素、行内/行内块元素、z-index为0元素、正z-index元素.

文本节点我们也看成是一个行内元素

判断层叠顺序

在理解了层叠上下文和元素层叠水平的概念后,现在我们就可以说说元素在Z轴上的层叠顺序到底是怎么回事了: 元素在Z轴上的层叠顺序取决于两个方面: 元素所在的层叠上下文、元素自身的层叠级别,如果抛开层叠上下文来判断元素在Z轴上的层叠顺序就是瞎胡闹

1. 当要比较的两个元素在同一层叠上下文时, 就按照元素自身的层叠级别, 如果级别相同时后则覆盖前者

  • 同一层叠上下文中的兄弟元素

    image-20210529225610214

    上图box1、box2都处在同一层叠上下文中(html元素形成的上下文) ,二者都是行内块元素,级别相同, 所以后者覆盖前者

    image-20210529230553734

    上图box1、box2也都处在同一层叠上下文中(html元素形成的上下文),但box1是行内元素,box2是块级元素, 根据元素的层叠级别,行内元素要高于块级元素, 所以box1显示在box2上面; 但是有个奇怪的现象, box1只能覆盖box2的背景,却不能覆盖box2内的字体.......为啥呢? 其实这个现象我们在上面也有提到过: 文本节点我们也看成是一个行内元素, 由于行内元素的级别要高于background/border,所以box1不能覆盖box2元素内的文本节点.

  • 同一层叠上下文中的父子元素

    也有可能是父子元素会出现在同一层叠上下文中, 其实刚刚上面我们说的字体的例子,就可以看成是父子元素在同一层叠上下文中, 这里就不在另外举例啦

2. 当要比较的两个元素不在同一父层叠上下文时, 需要先向上查找到两者所在的共同的且最近的层叠上下文,然后再根据第1条规则来判断

image-20210603193156153

看一下这个我从MDN上搬过来的例子: 根据我们上面说的层叠上下文的创建方式, 可知这里每个div元素都创建了自己的层叠上下文, 图中创建的层叠上下文共有3层, 他们依次是:Root(div1、div2、div3(div4、div5、div6));我们先从最内层div3创建的层叠上下文分析:

  • 因为div4、div5、 div6处于div3创建的同一层叠上下文中, 所以它们三个可以直接根据层叠级别比较

    ​ 由于这三个元素都设置了定位属性, 可以直接根据z-index值的大小来判断;在Z轴的顺序依次是div4>div6>div5

  • 因为div1、div2、 div3处于根元素创建的同一层叠上下文中, 所以它们三就可以直接根据层叠级别比较

    ​ 同上, 直接根据z-index值的大小来判断;在Z轴的顺序依次是div1>div3>div2

所以最终这些元素在页面Z轴上的层叠顺序依次是: div1>div3>div2>div4>div6>div5

我们根据上面的结果也可以验证: 想要比较不在同一父层叠上下文中的两个元素(div2、div6), 需要先上查找二者(div2、div6处于同一父层叠上下文(html)的祖先元素(div2、div3),然后根据彼此祖先元素(div2、div3的层叠顺序判断

参考文章

  1. 层叠上下文-MDN

  2. 理解CSS的 z-index属性