阅读 2649

前端必须掌握的「CSS层叠上下文」讲解 | 纯手工示例,包教包会

你们都不看的总集篇: 从零开始的大前端筑基之旅(深入浅出,持续更新~)
觉得不错就顺手点个赞吧

本文中所有示例均为 `html` 渲染构成,所以可以直接选中示例在控制台调试,推荐自己动手试试文章中列举的小提示,有助于加深理解 。有任何问题欢迎在评论中指出。

嗯。。。chrome --> 鼠标移动到示例上 --> 右击 --> 检查

大部分人在初步学完css后,对z-index的印象大概处于“z-index就是用来描述定义一个元素在屏幕Z轴上的堆叠顺序”。例如,如果A元素和B元素重叠了,这么说有点枯燥,换个说法,

  1. 如果漂亮妹子A和文字B注释重叠了
  2. 但是页面渲染的时候,文字处于图片之上,挡住你看妹子了
  3. 这时候,你可能会
    • 设置妹子的z-index为999
    • 设置文字的z-index为-1

就是这么差别对待。既然拿上面那一段举栗子,那就代表这种理解有问题,至少是不严谨的。

  1. z-index属性仅在定位元素(定义了position属性,且属性值为非static值的元素)上有效果。

  2. 元素在Z轴上的堆叠顺序,由元素所属的层叠上下文、元素的层叠水平、以及元素在文档中的顺序等共同决定,元素的z-index值的只是决定元素的层叠水平的条件之一。

这两条一定要记牢,下面会用到。

什么是“层叠上下文”

层叠上下文(stacking context),是HTML中一个三维的概念。它划分了某种领域或范围,在渲染规则层面,将内部与外部隔开,并赋予元素自身及内部区域某些特性。

在CSS2.1规范中,每个盒模型的位置是三维的,即元素除了在页面上沿X轴Y轴平铺,同时还拥有一个相对屏幕垂直的纵深,也就是表示层叠的Z轴

对于元素所展现的内容,一般情况下,元素在文档中顺序排列,互相排斥,我们察觉不到它们在Z轴上的层叠关系。而一旦元素发生堆叠,就会发现某个元素覆盖了另一个元素或者被另一个元素覆盖。

对于元素的层级结构来说,父元素与其子元素大部分情况下都是重叠的,只是一般情况下,父元素没有设置边框和背景,或者因为子元素没有突破父元素的宽高,我们一般也不会意识到元素的堆叠情况。

元素由于特定的css设置,拥有了层叠上下文后,就会变成层叠上下文元素;

层叠上下文元素有如下特性:

  1. 层叠上下文的层叠水平要比普通元素高;
  2. 层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的层叠上下文。
  3. 每个层叠上下文是自成体系的,当元素发生层叠的时候,整个元素被认为是在父层叠上下文的层叠顺序中。
  4. 每个层叠上下文和兄弟元素独立,也就是当进行层叠变化或渲染的时候,只需要考虑后代元素。
  5. 层叠上下文可以阻断元素的混合模式;

如何创建“层叠上下文”

层叠上下文大部分由一些特定的CSS属性创建。

  1. HTML中的根元素<html></html>本身就具有层叠上下文,称为“根层叠上下文”。
  2. 普通元素设置position属性为static值并设置z-index属性为具体数值,产生层叠上下文。
  3. css3补充了一些创建层叠上下文的条件,后面会列举。

层叠水平

层叠水平(stacking level),决定了同一个层叠上下文中元素在z轴上的显示顺序。

所有的元素都有层叠水平,包括层叠上下文元素。每个层叠上下文和兄弟元素独立,因此层叠水平的比较只有在当前层叠上下文元素中才有意义。

归属于不同层叠上下文的元素,首先要追溯其祖先元素的层叠上下文,直到各自的祖先层叠上下文元素归属于同一个层叠上下文,然后判断这两个祖先元素的层叠水平。

层叠顺序

层叠顺序(stacking order), 表示元素发生层叠时的特定的垂直显示顺序,是一种用于确定元素层叠水平的规则。

当元素重叠时,必须决定哪部分内容展示在屏幕上,因此,层叠顺序决定了一条优先级规则,也可以称为等级链规则。 以下排名分先后,自上而下优先级越来越低:

  1. z-index > 0 (要求z-index属性生效)
  2. z-index: auto 或者 z-index: 0 (z-index: auto 不会创建层叠上下文)
  3. inline、inline-block 行内盒子
  4. float浮动盒子
  5. block块级盒子,非 inline-block,无 position 定位(static除外)的子元素
  6. z-index < 0
  7. 层叠上下文元素的 background/border

当元素发生层叠的时候,其覆盖关系通常遵循下面2个准则:

  1. 在同一个层叠上下文领域,当具有明显的层叠水平标示的时候,如有效的 z-index 值,层叠水平值大的那一个覆盖小的那一个。
  2. 当元素的层叠水平一致、层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素。

深入浅出

概念讲解完了,下面进入实战环节,来,看我手上,现在我们有两只猫:

验证准则2,

当元素的层叠水平一致、层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素。

然后让它们重叠起来:

<div class="container">
  <div class="black">
    <img src="black.jpg"></img>  
  </div>
<div class="white">
    <img src="white.jpg"></img>  
  </div>
</div>
复制代码
.container{
  width: 300px;
  height: 200px;
  border: 1px solid grey;
}

.black{
  position: absolute;
}

.white{
  position: absolute;
  top: 50px;
  left: 50px;
}
复制代码

当前 black.imgwhite.img 都是普通元素, 其父元素也是普通元素,因此两者处于元素的“根层叠上下文”中。并且层叠水平一致,因此遵循后来居上原则,白猫覆盖黑猫。

验证准则1

在同一个层叠上下文领域,当具有明显的层叠水平标示的时候,如有效的 z-index 值,层叠水平值大的那一个覆盖小的那一个。

给黑猫black.img增加 z-index 属性并设置 position 使其生效

.black img{
  position: relative;
  z-index: 1;
}
复制代码

当前 black.img 是层叠上下文元素(不影响比较层叠水平), white.img 是普通元素, 其父元素都是普通元素,因此两者处于元素的“根层叠上下文”中。black.img 具有明显的层叠水平标示 z-index: 1。因此黑猫覆盖了白猫。

验证特性2、3

  1. 层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的层叠上下文。
  2. 每个层叠上下文是自成体系的,当元素发生层叠的时候,整个元素被认为是在父层叠上下文的层叠顺序中。

归属于不同层叠上下文的元素,首先要追溯其祖先层叠上下文元素,直到各自的祖先层叠上下文元素归属于同一个层叠上下文,然后判断这两个祖先元素的层叠水平。

接上面代码,首先给白猫white.img增加 z-index 属性并设置 position 使其生效,当前css代码如下:

.container{
  width: 300px;
  height: 200px;
  border: 1px solid grey;
}

.black{
  position: absolute;
}
.black img{
  position: relative;
  z-index: 1;
}

.white{
  position: absolute;
  top: 50px;
  left: 50px;
}

.white img{
  position: relative;
   z-index: 2;
}
复制代码

此时,black.imgwhite.img 都是层叠上下文元素(不影响层叠水平对比), 其父元素都是普通元素,两者处于元素的“根层叠上下文”中。black.imgwhite.img都具有明显的层叠水平标示 z-index,白猫的 z-index 值大于黑猫,于是重新覆盖在黑猫上面。

blackwhite补充z-index 属性,使blackwhite产生层叠上下文

.black{
  position: absolute;
  z-index: 2;
}
.white{
  position: absolute;
  top: 50px;
  left: 50px;
  z-index: 1;
}
复制代码

虽然白猫的 z-index 值大于黑猫,但由于各自的父元素均为层叠上下文元素,因此白猫和黑猫归属于不同的上下文元素,无法直接比较,需要追溯各自父元素。

blackwhite处于元素的“根层叠上下文”中,blackz-index 大于 white,因此black的后代元素black.img覆盖在white的后代元素white.img上,即黑猫覆盖白猫。

验证特性1

  1. 层叠上下文的层叠水平要比普通元素高;

为了使两张图片重叠,设置black的宽高为0。

如果继续使用position: absolute来使图片重叠,就没法验证本条,因为依据层叠顺序z-index: 0z-index: auto 本身就比inlineblock优先级高,体现不出层叠上下文元素比普通元素层叠水平高

html代码不变,css如下

.black{
  width :0;
  height :0;
}
// markdown 编辑器可能会补充一些属性,导致黑猫无法显示,因此我们补充下面属性
.black img{
  width: 232px;
  height: 153px;
  max-width: inherit; 
}
复制代码

上面的示例中,白猫和黑猫都是普通元素,处于元素的“根层叠上下文”中,其中白猫在DOM流中处于后面,因此白猫在黑猫的上面。

下面我们使用css3中补充的创建上下文的条件来使black元素产生层叠上下文。

元素的opacity属性值不是1

.black{
  width :0;
  height :0;
  opacity: 0.9;
}
复制代码

现在, black 元素是层叠上下文元素,其后代black.img的层叠水平大于普通元素,所以黑猫覆盖了白猫。如果你仔细看,还是可以看到黑色背景后面的白猫的。

验证层叠顺序:z-index: auto > inline/inline-block

上个示例提到, z-index: 0z-index: auto 本身就比inlineblock优先级高,我们来验证一下。整体css属性如下。

.black img{
  position: absolute;
}
复制代码

仅设置black.imgposition: absolute;。不显式设置 z-index 数值时,浏览器会默认z-index: auto

当前 black.imgwhite.img 都是普通元素(z-index: auto 不会创建层叠上下文), 其父元素也是普通元素,因此两者处于元素的“根层叠上下文”中。元素无明显的层叠水平标示 z-index,依据层叠顺序,z-index: auto > inline/inline-block,黑猫在上面。

如果对白猫 white.img 追加position: absolute; 属性,black.imgwhite.img 就处于同一层叠水平,依据后来居上原则,白猫在上面。选中本示例在控制台中调试一下吧

验证层叠顺序:z-index < 0 > 层叠上下文元素的 background/border

在此之前,我们先验证另一个规则:block块级盒子 > z-index < 0 现在,你只有黑猫了。

<div class="container">
  <div class="black">
    <img src="black.jpg"></img>  
  </div>
</div>
复制代码

black设置背景色橙色, black.img 设置z-index: -1

.black{
  background: orange;
}
.black img{
  position: relative;
  z-index: -1;
}
复制代码

整个black元素都显示橙色,证明了块级元素的层叠水平比z-index: -1 高。 然后给black元素追加opacity: 0.9,使black产生层叠上下文。

.black{
  background: orange;
  opacity: 0.9;
}
.black img{
  position: relative;
  z-index: -1;
}
复制代码

此时,图片出现在橙色背景之上,验证了z-index < 0 > 层叠上下文元素的 background/border

css3产生层叠上下文的规则

在CSS3中,元素属性满足以下条件之一,也会产生层叠上下文。

  1. 父元素的display属性值为flex|inline-flex,子元素z-index属性值不为auto的时候,子元素为层叠上下文元素(此时z-index会生效,不需要设置position);
  2. 元素的opacity属性值不是1
  3. 元素的transform属性值不是none
  4. 元素mix-blend-mode属性值不是normal
  5. 元素的filter属性值不是none
  6. 元素的isolation属性值是isolate
  7. will-change指定的属性值为上面任意一个;
  8. 元素的-webkit-overflow-scrolling属性值设置为touch`。

4-8条可自行百度或点击参考文档1

验证css3规则1

父元素的display属性值为flex|inline-flex,子元素z-index属性值不为auto的时候,子元素为层叠上下文元素(此时z-index会生效,不需要设置position);

<div class="container">
  <div class="black">
    <img src="black.jpg"></img>  
  </div>
</div>
复制代码

black设置背景色橙色, black.img 设置z-index: -1

.black{
  background: orange;
}
.black img{
  position: relative;
  z-index: -1;
}
复制代码

使用规则1,补充css属性如下:

.container{
  width: 300px;
  height: 200px;
  border: 1px solid grey;
  display: flex;
}
.black{
  background: orange;
  z-index:2; 
}
复制代码

此时,图片出现在橙色背景之上,依据层叠顺序z-index < 0 > 层叠上下文元素的 background/border,反向推出此时black为层叠上下文元素。

白猫还你

现在,把白猫还给你,使用下面的示例在控制台验证一下各项规则和你的想法吧

嗯。。。chrome --> 鼠标移动到示例上 --> 右击 --> 检查

如果你收获了新知识,或者收获了左侧精美图片,请点个吧~

-

总集篇: 从零开始的大前端筑基之旅(深入浅出,持续更新~)

推荐阅读:

参考文章:

  1. 深入理解CSS中的层叠上下文和层叠顺序