CSS-你不知道的Z-Index

525 阅读7分钟

z-index的问题在于很少有人理解它真正的工作原理

它并不复杂,但是如果你没有真正的花时间去阅读它的规范,肯定存在一些你完全不知道的关键点

如果不相信我的话可以看看你怎么解决下面这个问题

问题

在下面的HTML中, 你有三个<div>元素,每个<div>包含一个<span>元素。每个<span>都有一个背景色--分别为红色、绿色和蓝色。每个<span>的位置绝对定位到文档的左上方,与其他<span>元素稍微重叠,这样你就可以看到哪些元素堆叠在哪些元素的上面。第一个<span>的z-index值为1,而其他两个没有设置z-index。 下面是HTML和基本CSS代码。这个链接是CodePen可视化的演示,下面是完整的代码。

HTML

<div>
  <span class="red">Red</span>
</div>
<div>
  <span class="green">Green</span>
</div>
<div>
  <span class="blue">Blue</span>
</div>

CSS

.red, .green, .blue {
  position: absolute;
}
.red {
  background: red;
  z-index: 1;
}
.green {
  background: green;
}
.blue {
  background: blue;
}

挑战

尝试看看你能否让红色<span>元素堆叠在蓝色和绿色<span>元素下面,不能违反以下任何规则。

  • 不要用任何方式修改HTML
  • 不要增加或者改变任何元素的z-index
  • 不要增加或改变任何元素的position属性

这里是解决方案, 可以先尝试自己解决

问题答案

解决办法是在第一个<div>(红色<span>的父级)中添加一个小于1opacity

div:first-child {
  opacity: .99;
}

你可能会不相信opacity会对元素的堆叠有影响,文章的余下部分会让事情变得清楚一些

堆叠顺序

z-index似乎很简单:z-index较大的元素堆叠在z-index较小的元素上面,是这样吗,其实不是。这就是z-index。它看起来很简单,所以大多数开发者不会花时间去阅读规则。

HTML文档中的每一个元素都可以位于文档中其他元素的上面或下面。这就是所谓的堆叠顺序。确定这种顺序的规则在规范中定义得非常清楚。

当不涉及z-index和position属性时,规则非常简单:基本上,堆叠顺序和HTML中的出现顺序是一样的。(其实比这更复杂一些,但只要你不使用负页边距来重叠内联元素,你可能就不会遇到边缘情况)。 

当你把position属性引入到组合中时,任何"positioned "的元素(以及它们的子元素)都会显示在任何非定位元素的上面。(说一个元素是 "positioned "的意思是它的position不是static的,例如,absoluterelative,等等)。 

起初,人们很自然地认为z-index值较大的元素在z-index值较小的元素上面,任何有z-index的元素在任何没有z-index的元素上面,但事实并非如此简单。首先,z-index只对定位的元素有效。如果你试图在一个没有指定position的元素上设置z-index,它将毫无作用。其次,z-index值可以创建堆叠上下文,现在看来看似简单的事情变得复杂了。

堆叠上下文

在堆叠顺序中向前或向后移动的具有共同父元素的一组元素构成了所谓的堆叠上下文。充分理解堆叠上下文是真正理解z-index和堆叠顺序含义的关键。 

每个堆叠上下文都有单个的HTML元素作为其根元素。当一个新的堆叠上下文在一个元素上形成时,该堆叠上下文会将其所有的子元素限制在堆叠顺序中的一个特定位置。这意味着,如果一个元素被包含在堆叠顺序最底层的堆叠上下文中,那么即使它的z-index是100000000,也无法让它出现在堆叠顺序更高的另一个堆叠上下文中的元素上面。简单理解就是只要祖先堆叠上下文的堆叠循序较低, 无论子元素的z-index多大, 其堆叠顺序都无法超过祖先同级元素的堆叠顺序较大的元素

新的堆叠上下文可以通过三种方式之一在一个元素上形成。

  • 当一个元素是一个文档的根元素(<html>元素)

  • 当一个元素的position不是static的,z-index值不是auto的时候

  • 当一个元素的opacity值小于1时

第三种方式(opacity)几乎从未在w3c规范文档之外被提及。

除了不透明度,几个较新的CSS属性也会创建堆叠上下文。这些属性包括:transformfilterscss-regionspaged media以及可能的其他属性。作为一般规则,如果一个CSS属性需要在屏幕外的上下文中渲染,它似乎必须创建一个新的堆叠上下文。

确定元素在堆叠顺序中的位置

同一堆叠上下文中的堆叠顺序

以下是确定单个堆叠上下文中堆叠顺序的基本规则(以下顺序由低到高)。

  •  堆叠上下文的根元素

  • 定位的元素(和它们的子元素)的z-index值为负数(较高的值堆叠在较低的值前面;相同值的元素根据在HTML文档中的出现顺序排序)

  • 非定位元素(根据HTML文档中的出现顺序排序)

  • 定位的元素(和它们的子元素)的z-index值为auto(根据HTML文档中的出现顺序排序)

  • 定位的元素(和它们的子元素)具有正的z-index值(较高的值堆叠在较低的值前面;具有相同值的元素根据HTML文档中的出现顺序排序)

注意

在堆叠上下文中,具有负z-index的定位元素在堆叠顺序中排在第一位,这意味着它们出现在所有其他元素的下面。正因为如此,一个元素有不可能出现在它自己的父元素下面。只有当元素和父元素在同一个堆叠上下文中,并且父元素不是该堆叠上下文的根元素时,这种情况才会发生。

总结&回顾

回到最初的问题上,我重新创建了HTML结构,在每个标签中添加了注释,表示其在堆叠顺序中的位置(假设还是原来的CSS)。  

<div><!-- 1 -->
  <span class="red"><!-- 6 --></span>
</div>
<div><!-- 2 -->
  <span class="green"><!-- 4 --><span>
</div>
<div><!-- 3 -->
  <span class="blue"><!-- 5 --></span>
</div>

当我们将opacity添加到第一个<div>中时,堆叠顺序会发生这样的变化。

<div><!-- 1 -->
  <span class="red"><!-- 1.1 --></span>
</div>
<div><!-- 2 -->
  <span class="green"><!-- 4 --><span>
</div>
<div><!-- 3 -->
  <span class="blue"><!-- 5 --></span>
</div>

span.red由原来的6变为1.1,这里其实已经形成了一个新的堆叠上下文,第一个<div>就是新的堆叠上下文的根元素,span.red是新的堆叠上下文的第一个元素

现在你应该能更清楚为什么红色盒子会移动到其他盒子下面。原来的例子只包含两个堆叠上下文,根上下文和span.red上形成的上下文。当我们为 span.red 的父元素添加不透明度时,我们形成了第三个堆叠上下文,因此,span.red 上的 z-index 值仅适用于该新上下文(第三个堆叠上下文)。因为第一个<div>(我们应用了opacity的那个)和它的同级元素没有设置position或z-index值,它们的堆叠顺序是由它们在HTML中的出现顺序决定的,这意味着第一个<div>和它的堆叠上下文中包含的所有元素都被呈现在第二个和第三个<div>元素的下面。

广告

字节广告系统基建部门招人

组件库(移动,PC,Vue3,react,Vue2,TypeScript),图表库,serverless

欢迎投递简历,多多益善😁

邮箱📮:limantang.neo@bytedance.com

文章参考

philipwalton.com/articles/wh…

developer.mozilla.org/en-US/docs/…

www.w3schools.com/cssref/pr_p…