z轴的层叠顺序

提到css中的z轴,相信大家都对z-index是最熟悉的,也是最常使用的。但是在z轴的世界里,z-index只是组成的一小部分而已。z轴中的元素发生重叠时,会遵循一个规则:层叠顺序。上周的博客里简单地提到了层叠顺序,本周在做一些小demo时发现有很多小细节知识点,因此分享给大家。 提到了层叠,就先来了解一下层叠上下文和层叠水平的概念。

层叠上下文

MDN对层叠上下文的概念是:层叠上下文是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸,HTML元素依据其自身属性按照优先级顺序占用层叠上下文的空间。层叠上下文的概念和块状格式化上下文相似,可以认为是一种特别的布局环境。那么,一个元素满足什么条件时就可以创建层叠上下文呢?

  • 根元素html就是一个层叠上下文。
  • z-index不为auto的相对/绝对定位元素。
  • 父元素display为flex或者inline-flex,子元素z-index值不为auto时,子元素会创建层叠上下文。
  • 元素opacity不为1。
  • 元素mix-blend-mode(混合模式)值不为normal。
  • 元素filter(滤镜)值不为none。
  • 元素isolation(隔离)值不为isloate。
  • 元素will-change(提前告知浏览器值的变化)生效时。
  • 元素-webkit-overflow-scrolling(控制元素在移动设备上是否使用滚动回弹效果)值设为touch。
  • 元素perspective(指定了观察者与z=0平面的距离)值不为none。

上述从第三条开始,都是css3相关属性对层叠上下文的创建影响。说了这么多,层叠上下文到底有什么作用?层叠上下文的元素在z轴上排列顺序高于普通元素,这点在后面会再提到。发生层叠时,元素之间的排序必须在一个层叠上下文中比较才有意义!

层叠水平

“层叠水平”英文称作”stacking level”,决定了同一个层叠上下文中元素在z轴上的显示顺序。与层叠上下文不同,只有满足特定条件才能创建层叠上下文,但html中所有的元素都有层叠水平,包括层叠上下文的元素也是有层叠水平的。普通元素的层叠水平受限于其所处于的层叠上下文,因此多个元素发生层叠时,一定要在这几个重叠元素共有的层叠上下文中比较!否则是没有意义的。说到这里了,该如何比较顺序呢?这就要回到一开始提到的层叠顺序了!

层叠顺序

上面讲到了层叠上下文和层叠水平,这两个都属于概念。但是层叠顺序不一样,它是一种规定,z轴的世界发生重叠冲突时需要遵守层叠顺序规则。让我们再好好看一下这张图,会不会有人跟我有一样的两点疑问:1、为什么inline水平盒子的层叠顺序还高于block块状水平盒子?2、前面提到了层叠上下文的元素在z轴上排列顺序高于普通元素,为什么在这张图里最低的顺序就是层叠上下文啊?

首先关于第一点,在页面布局时会觉得块状盒子和浮动盒子会更厉害一些,但是我们一般使用块状盒子和浮动盒子用于页面布局,而inline盒子都是内容,在页面中内容肯定是要比布局要更重要一些的,因此inline盒子的层叠顺序会更高一些。关于内容>布局这一方面,后面会举例证明这一点。关于第二点,仔细看图,会发现“不依赖z-index的层叠上下文”排在第二,而排在最后的也是层叠上下文。其实这点很挺让人混淆的,在经过demo测试后,会发现这两个层叠上下文不是同一个元素!“不依赖z-index的层叠上下文”元素是需要与其他元素重叠时需要进行对比层叠顺序的,而“层叠上下文background/border”元素是发生层叠的元素所共有的父类层叠上下文元素的background/border!

看到这里都只是一些概念,在列举demo帮助大家笑话层叠顺序之前,有两点黄金准则需要牢记:

1、层叠水平大的覆盖层叠水平小的,前提是在同一个层叠上下文中比较。

2、后来者居上,如果层叠水平相同,那么在dom树中靠后的会覆盖靠前的。

现在通过几个demo来更好地理解层叠顺序吧!

  • demo1:
 <div style="position:relative; z-index:auto;">
    <p style="background-color: red;padding: 20px;position: relative;z-index: 2;">第一个p标签</p>
</div>
<div style="position:relative; z-index:auto;">
    <p style="background-color: yellow;padding: 20px;margin-top: -20px;position: relative;z-index: 1;">第二个p标签</p>
</div>

第二个p标签由于margin-top: -20px;使得两个p标签发生重叠。因此需要比较这两个p标签的层叠顺序,那么首先就是需要找到共有的层叠上下文才能进行比较。回顾上面提到的如何创建层叠上下文,会发现两个div都没有创建层叠上下文,那怎么办?别忘了,html标签天生就是一个层叠上下文呀,因此在html层叠上下文下,index为2的p标签自然覆盖了index为1的p标签。

  • demo2:
 <div style="position:relative; z-index:0;">
    <p style="background-color: red;padding: 20px;position: relative;z-index: 2;">第一个p标签</p>
</div>
<div style="position:relative; z-index:0;">
    <p style="background-color: yellow;padding: 20px;margin-top: -20px;position: relative;z-index: 1;">第二个p标签</p>
</div>

demo2与demo1的区别仅仅在于div元素的z-index变为0了,欧吼,这样的话两个div元素满足创建层叠上下文的要求了。此时第一个p标签可以和第二个p标签比较了吗?当然不行啦。前面一直强调元素之间的排序必须在一个层叠上下文中比较,因此需要逐级往上寻找层叠上下文,此时会发现两个div的共有层叠上下文是html标签。回到层叠顺序的图片,会发现这两个div元素都是不依赖z-index的层叠上下文元素,那么此时第二点黄金准则就发挥作用了:后来者居上。因此第二个p标签覆盖第一个p标签!(注意:z-index为0或者z-index为auto的即为不依赖z-index)

  • demo3:
 <div style="position:relative; z-index:0;">
    <p style="background-color: red;padding: 20px;position: relative;z-index: 2;">第一个p标签</p>
</div>
<div style="position:relative; z-index:auto;">
    <p style="background-color: yellow;padding: 20px;margin-top: -20px;position: relative;z-index: 1;">第二个p标签</p>
</div>

demo3中又将第二个div的z-index设为了auto,此时该div不满足创建层叠上下文的条件。因此第二个p标签往上只能找到html层叠上下文。第一个p标签找到的第一个层叠上下文即为父元素div,但是这个层叠上下文不包含第二个p标签,因此继续往上寻找,即为html层叠上下文。此时需要比较的就是第一个div和第二个p标签的层叠顺序了!由层叠顺序图可以发现,正index>不依赖z-index的层叠上下文元素,因此p标签覆盖在div之上!

那么如果是第一个div元素的z-index为auto,第二个div元素的z-index为0呢?此时是谁覆盖谁呢?

由上面3个demo可以得出这两条结论:

1、层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的层叠上下文。demo3中的第一个p标签受限于父div元素,最后比较层叠顺序时比较的也是层叠上下文div元素和第二个p标签元素。

2、比较层叠顺序的元素并不一定是页面上发生层叠的元素。

  • demo4:
<div class="d0" style="opacity: 0.9">d0
  <div class="d1">
      <div class="d2" style="position:relative; z-index:0;">
          <p class="p1" style="background-color: red;padding: 20px;">第一个p标签</p>
      </div>
  </div>
  <div class="d3">
      <p class="p2" style="background-color: yellow;padding: 20px;margin-top: -30px;">第二个p标签</p>
  </div>
</div>

demo4中,d0和d2是层叠上下文。p1与p2发生层叠,且共有的层叠上下文是d0。那么此时需要比较的是d1和p2标签吗?不是的,此时需要比较的是d2和p标签。也就是说比较的是离d0最近的层叠上下文元素或者层叠元素本身。因此d2层叠顺序>p2层叠顺序,p1覆盖在p2之上。

  • demo5:
 <div class="d1" style="width: 100px;height: 100px;background: red;color: white;">d1</div>
<div class="d2" style="width: 100px;height: 100px;background: green; margin-top: -90px;">d2</div>

可以看到,虽然d1覆盖在d2之上,但是d1的内容依然可以显示在d2之上。说明内容优先级>背景,但是需要注意的是对于inline/inline-block元素而言,其背景>内容。

  • demo6:
<div class="d0" style="filter: blur(0.1px);background: yellow;padding-left: 10px;">
  <div class="d1" style="width: 100px;height: 100px;background: red;color: white;transform: scale(1);">d1</div>
  <div class="d2" style="width: 100px;height: 100px;background: green; margin-top: -90px;">d2</div>
</div>

demo6中,d0和d1都是层叠上下文。根据层叠顺序图,位于最底层的是层叠上下文的background/border,此时d0符合条件位于最底下。d1是不依赖z-index的层叠上下文,d1层叠顺序>d2层叠顺序,因此d1覆盖d2。因此层叠顺序图中最底层的“层叠上下文的background/border”指的是祖先层叠上下文元素。

那么,依赖z-index的层叠上下文应该如何排序呢?结论是:

1、如果层叠上下文元素不依赖z-index数值,则其层叠顺序是z-index:auto可看成z:index:0级别;

2、如果层叠上下文元素依赖z-index数值,则其层叠顺序由z-index值决定。

关于层叠顺序,目前我的学习还没有结束,还有一些奇怪的现象无法理解。在这里先简单分享给大家,如果有新的知识点会继续补充这片文章。

关于 “z轴的层叠顺序” 的 1 个意见

发表评论