z-index不生效?让我们来掀开它的面具

1,334 阅读7分钟

我正在参加「掘金·启航计划」

涅槃计划CSS篇

前言

hi大家好,我是小鱼,今天复习的是z-index。之前以为自己很了解它,可是在工作中总会遇到一些不思其解的问题,后来去深入学习了层叠上下文层叠等级层叠顺序,才发现z-index只是其中的一叶小舟,今天就一起来看看它背后到底隐藏着什么。

z-index

.container {
  z-index: auto | <integer> ;
}
  • z-index 属性是允许给一个负值的。
  • z-index 属性支持 CSS3 animation 动画。
  • 在 CSS 2.1 的时候,需要配合 position 属性且值不为 static 时使用。

这个属性大家应该都很熟悉了,指定了元素及其子元素的 在 Z 轴上面的顺序,而 Z 轴上面的顺序 可以决定当元素发生覆盖的时候,哪个元素在上面。 z-index值大的元素会覆盖较低的。

不知道大家在工作中有没有遇到过这种情况,明明给其设置了z-index并且也设置了position不为static,但是样式并不是你所想的那样。可能这里大家对z-index不太了解,判断元素在Z轴上的顺序,不仅仅是z-index值的大小,接下来给大家解释层叠上下文层叠等级层叠顺序

image.png

层叠上下文

层叠上下文(stacking context),是HTML中一个三维的概念。如果一个元素含有层叠上下文,我们可以理解为这个元素在Z轴上就“高人一等”。

大家应该都玩过王者荣耀,里面的段位就是一个层级概念。你可以把「层叠上下文」理解为上了最强王者的人,还有很多没上王者的人,我们可以看成是菜鸡。那王者选手和菜鸡之间就形成了一个差距,这个差距也就是在Z轴上的距离,王者选手离荣耀王者就更近了一步,这里的“荣耀王者”可以看成是我们的屏幕观察者。

这样抽象解释完大家应该明白了什么是层叠上下文。继续往下看

层叠等级

层叠等级(stacking level),决定了同一个层叠上下文中元素在Z轴上的显示顺序。这里又牵扯出一个level,那么这个等级指的又是什么呢?

所有的元素都有层叠等级,包括层叠上下文元素,层叠上下文元素的层叠等级可以理解为是什么普通王者,无双,荣耀传奇之类。然后,对于普通元素的层叠等级,我们的探讨仅仅局限在当前层叠上下文元素中。为什么呢?因为否则没有意义。

还是回到王者荣耀,元素具有层叠上下文就相当于是王者段位,但是王者里面又分为普通王者,无双王者和荣耀王者还有传奇王者,那我们如果拿普通王者的韩信和传奇王者的韩信相比较实际上是没有意义的,那不吊打吗,那他牛不牛逼是由段位决定的(排除一些意外情况哈哈哈)。

层叠上下文的创建

说白了就是一个元素如何才能变成层叠上下文元素?

层叠上下文是由一些特点的CSS属性创建的,分为三点:

  1. 页面根元素天生具有层叠上下文,称之为“根层叠上下文”。
  2. 普通元素设置position属性为static值并设置z-index属性为具体数值,产生层叠上下文。
  3. 其他CSS3中的新属性也可以
    1. flex 容器的子元素,且 z-index 值不为 auto
    2. grid 容器的子元素,且 z-index 值不为 auto
    3. opacity 属性值小于 1 的元素
    4. transform 属性值不为 none 的元素
    5. filter 属性值不为 none 的元素
    6. isolation 属性值为 isolate 的元素
    7. -webkit-overflow-scrolling 属性值为 touch 的元素;

简单写两个例子

栗子一

.box1,.box2 {
  position: relative;
  width: 100px;
  height: 100px;
}
.a,.c {
  width: 100px;
  height: 100px;
  position: absolute;
  font-size: 20px;
  padding: 5px;
  color: white;
  border: 1px solid rgb(119, 119, 119);
}
.a {
  background-color: rgb(0, 163, 168);
  z-index: 1;
}
.c {
  background-color: rgb(0, 168, 84);
  z-index: 2;
  left: 50px;
  top: -50px;
}

<div class="box1">
    <div class="a">A</div>
</div>
<div class="box2">
    <div class="c">C</div>
</div>

image.png

因为box1,box2都没有设置z-index,所以没有创建层叠上下文,所以其子元素都处于‘根层叠上下文’中,在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个。

栗子2

只帖了修改部分

.box1 {
  z-index: 2;
}
.box2 {
  z-index: 1;
}

.a {
  background-color: rgb(0, 163, 168);
  z-index: 1;
}
.b {
  background-color: rgb(21, 84, 180);
  z-index: 2;
  left: 50px;
  top: 50px;
}
.c {
  background-color: rgb(0, 168, 84);
  z-index: 999;
  left: 100px;
  top: 50px;
}

<div class="box1">
    <div class="a">A</div>
    <div class="b">B</div>
</div>
<div class="box2">
    <div class="c">C</div>
</div>

image.png

大家可以发现我们给C盒子设置的z-index为999远大于A、B两个盒子,效果却出现在他俩下面。那是因为给两个父盒子分别设置了z-index,创建了两个不同的层叠上下文,而box1的z-index值大,所以排在上面,这里验证了层叠等级。

栗子3

有一个父元素绝对定位,它有一个子元素也是绝对定位,父元素z-index大于子元素z-index,为何子元素还是在父元素的上面?如何让这个子元素放在父元素的下面。

.parent {
    width: 100%;
    height: 500px;
    background-color: rgb(243, 151, 45);
    position: absolute;
    z-index: 1;
}

.child {
    width: 20%;
    height: 150px;
    background-color: rgb(211, 56, 56);
    position: absolute;
    z-index: 0;
}

<div class="parent">
    <div class="child">C</div>
</div>

效果却是这样

image.png

解决方案

  1. 因为父元素和子元素之间,z-index是无法对比的,同级之间的z-index才能对比。可以考虑换一种方式,两个div做同级,外面包一层父元素,根据共同的父元素定位、做层级区分就可以。

  2. 父元素不指定 z-index, 而子元素 z-index 为 -1

结论

普通元素的层叠等级优先由层叠上下文决定,所以,层叠等级的比较只有在当前层叠上下文元素中才有意义。

层叠顺序

层叠顺序(stacking order),表示元素发生层叠时候有着特定的垂直显示顺序,注意,这里跟上面两个不一样,上面的层叠上下文和层叠等级是概念,而这里的层叠顺序是规则

上图

image.png

在不考虑CSS3的情况下,当元素发生层叠时,层叠顺序遵循上面图中的规则。

这里稍微解释下为什么内联元素的层叠顺序要比浮动元素和块状元素都高?有些同学可能觉得浮动元素和块状元素要更屌一点,图中我标注了内联样式是内容,因为网页中最重要的是内容,文字和浮动图片的时候优先确保显示文字。

层叠准则

  1. 谁大谁上: 当具有明显层叠等级的时候,在同一个层叠上下文领域,z-indx大的那一个覆盖小的那一个。
  2. 后来居上: 当元素的层叠等级一致、层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素。

栗子4

.box1,.box2 {
  position: relative;
  width: 100px;
  height: 100px;
}
.box1 {
  z-index: 0;
}
.box2 {
  z-index: 0;
}
.a,.c {
  width: 100px;
  height: 100px;
  position: absolute;
  font-size: 20px;
  padding: 5px;
  color: white;
  border: 1px solid rgb(119, 119, 119);
}
.a {
  background-color: rgb(0, 163, 168);
  z-index: 999;
}
.c {
  background-color: rgb(0, 168, 84);
  z-index: 1;
  left: 50px;
  top: -50px;
}

<div class="box1">
    <div class="a">A</div>
</div>
<div class="box2">
    <div class="c">C</div>
</div>

image.png

上面给两个父盒子都设置了z-index为0,这里要注意z-index一旦变成数值,哪怕是0,都会创建一个层叠上下文。当然层叠规则就发生了变化,子元素的层叠顺序比较变成了优先比较其父级的层叠上下文的层叠顺序,尽管a盒子的z-index为999。又由于两个父级都是z-index:0,层叠顺序这一块一样大,这个时候就遵循后来居上原则,根据DOM流中的位置决定谁在上面。也可以说子元素上面的z-index失效了!

end

回顾自己以前使用z-index都不太规范或者滥用,以后一定改正!

image.png