浅谈层叠上下文

268 阅读5分钟

在CSS2.1规范中,盒模型的位置是三维而不是平面的,分别是x轴、y轴和表示层叠的z轴,层叠上下文即元素在某个层级上z轴方向的排列关系。

本文会涉及在以下几个重要的概念:

  • 层叠上下文(Stacking Context)
  • 层叠等级(Stacking Level)
  • 层叠顺序(Stacking Order)
  • z-index

层叠上下文(Stacking Context)

我们可以想象有一张桌子,上面放了一些东西,这张桌子就代表着一个层叠上下文。而旁边的另一张桌子,就代表着另一个层叠上下文,而这两张桌子目前来看没有交集。

现在,第一张桌子上面放了几个方块,这几个方块上叠了一片玻璃板,而玻璃板上放了几个苹果。这些方块、玻璃板、苹果各自代表着这个桌子(层叠上下文)中不同的层叠层。

回到实际中,每一个网页都有一个默认的叠层上下文,即html文档根元素。

html标签中的一切都被放置在这个默认的层叠上下文中的一个层叠层上。

当我给一个定位元素(position属性)赋予了除z-index: auto以外的值时,就是创建了另一个桌子(层叠上下文),相当于“多了另一张桌子”。

image.png

层叠上下文1(Stacking Context1)是由html根元素形成的默认层叠上下文,而层叠上下文2、3(Stacking Context2,Stacking Context3)都是层叠上下文1(Stacking Context1)中的层叠层,他们各自形成了新的层叠上下文,并包含了属于自己的层叠层。

现在我们了解了什么是层叠上下文,那么如何才能形成层叠上下文呢:

- 文档根元素
- position 值为 absolute | relative,并且 z-index 值不为 auto
- position 值为 fixed | sticky
- z-index 值不为 auto 的 flex 元素
- opacity 值小于 1 的元素
- transform 值不为 none 的元素
- will-change 中指定了任意CSS属性
- filterperspectiveclip-pathmask 值不为 none 的元素

总结:

  • 层叠上下文可以包含在其他层得上下文中
  • 每个层叠上下文完全独立于他的兄弟元素,处理层叠时只会考虑他的子元素

层叠等级(Stacking Level)

  • 在同一个层叠上下文中,他描述定义这个层叠上下文中的层叠上下文元素在z轴上的上下顺序。
  • 在其他普通元素中,他描述定义这些元素在z轴上的上下顺序

总结:

  • 普通元素的层叠等级优先由他所在的层叠上下文决定。
  • 层叠等级的比较只有在当前层叠上下文元素中才有意义,不同层叠上下文中比较层叠等级是毫无意义的。

但是注意,不要把层叠等级与z-index属性混为一谈,很多编程人员片面的认为只要将z-index设置的够大,就离屏幕观察者越近,然而这是不对的,虽然z-index在某种情况下确实可以决定元素层叠等级(会在后文详细分析),但是层叠顺序实际是由层叠上下文层叠等级共同决定

层叠顺序(Stacking Order)

上文所说的层叠上下文层叠等级是一种概念,而这里的层叠顺序则是规则了,他表示在一个层叠上下文中的元素会以一种特定的顺序规则在z轴上排列。

这是层叠顺序规则图 image.png

在比较元素上下顺序时,请根据这样的规则进行判断:

  • 首先比较两个元素是否处于同一个层叠上下文中
    • 如果是,那谁的层叠等级大,谁再上面(规则如上图所示)
    • 如果不是,那根据所属层叠上下文的层叠等级决定
  • 如果两个元素的层叠等级、层叠顺序均相同,所处DOM结构后面的元素层叠等级大于前面的元素

z-index

关于z-index与层叠等级的区别与实际影响,我们可以看下面这个例子:

<style>
  div {
    position: relative;
    width: 100px;
    height: 100px;
  }
  p {
    position: absolute;
    font-size: 20px;
    color: #fff;
    width: 100px;
    height: 100px;
  }
  .a {
      z-index: 1;
      background-color: #3939e9;
  }
  .b {
      z-index: 2;
      top: 40px;
      left: 40px;
      background-color: #40dd40;
  }
  .c {
      z-index: 3;
      top: -10px;
      left: 80px;
      background-color: #e63d3d;
  }
</style>

<body>
  <div class="box1">
    <p class="a">a</p>
    <p class="b">b</p>
  </div>

  <div class="box2">
    <p class="c">c</p>
  </div>
</body> 

image.png

因为父元素div并没有设置z-index,所以不会产生层叠上下文,p.a、p.b、p.c因为设置了position与z-index,所以各自产生了层叠上下文,但是他们都处于html根元素层叠上下文中,属于同一个层叠上下文,所以谁的z-index大,谁在上面

这就是上文中所说的z-index可以决定层叠等级的情况,只对定位元素position有效

那我们把代码改动一下:


<style>
  div {
    position: relative;
    width: 100px;
    height: 100px;
  }
  p {
    position: absolute;
    font-size: 20px;
    color: #fff;
    width: 100px;
    height: 100px;
  }
  .box1 {
    z-index: 2;
  }
  .box2 {
    z-index: 1;
  }
  .a {
      z-index: 1;
      background-color: #3939e9;
  }
  .b {
      z-index: 2;
      top: 40px;
      left: 40px;
      background-color: #40dd40;
  }
  .c {
      z-index: 9999;
      top: -10px;
      left: 80px;
      background-color: #e63d3d;
  }
</style>

<body>
  <div class="box1">
    <p class="a">a</p>
    <p class="b">b</p>
  </div>

  <div class="box2">
    <p class="c">c</p>
  </div>
</body> 

我们做了两个小小的改动: 对父元素的box1、box2设置了各自的z-index,把p.c的z-index值调整到了9999

image.png

可以看到,虽然p.c的z-index值为9999,远大于p.a和p.b的z-index值,但是他仍然在他两的下面。这是因为我们对box1、box2设置了z-index后,父元素div产生了层叠上下文,因为box1的z-index > box2的z-index值,所以box1永远会在box2的上面,box2所包含的p.c不论有多大的z-index值,都只能在box1所有元素的下面

所以在实际项目中,更改某些元素的z-index值缺没有获得想要的层叠顺序时,就应该考虑他们所处的层叠上下文关系了