在CSS2.1规范中,盒模型的位置是三维而不是平面的,分别是x轴、y轴和表示层叠的z轴,层叠上下文即元素在某个层级上z轴方向的排列关系。
本文会涉及在以下几个重要的概念:
- 层叠上下文(Stacking Context)
- 层叠等级(Stacking Level)
- 层叠顺序(Stacking Order)
- z-index
层叠上下文(Stacking Context)
我们可以想象有一张桌子,上面放了一些东西,这张桌子就代表着一个层叠上下文。而旁边的另一张桌子,就代表着另一个层叠上下文,而这两张桌子目前来看没有交集。
现在,第一张桌子上面放了几个方块,这几个方块上叠了一片玻璃板,而玻璃板上放了几个苹果。这些方块、玻璃板、苹果各自代表着这个桌子(层叠上下文)中不同的层叠层。
回到实际中,每一个网页都有一个默认的叠层上下文,即html文档根元素。
html标签中的一切都被放置在这个默认的层叠上下文中的一个层叠层上。
当我给一个定位元素(position属性)赋予了除z-index: auto以外的值时,就是创建了另一个桌子(层叠上下文),相当于“多了另一张桌子”。
层叠上下文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属性
- filter、perspective、clip-path、mask 值不为 none 的元素
总结:
- 层叠上下文可以包含在其他层得上下文中
- 每个层叠上下文完全独立于他的兄弟元素,处理层叠时只会考虑他的子元素
层叠等级(Stacking Level)
- 在同一个层叠上下文中,他描述定义这个层叠上下文中的层叠上下文元素在z轴上的上下顺序。
- 在其他普通元素中,他描述定义这些元素在z轴上的上下顺序
总结:
- 普通元素的层叠等级优先由他所在的层叠上下文决定。
- 层叠等级的比较只有在当前层叠上下文元素中才有意义,不同层叠上下文中比较层叠等级是毫无意义的。
但是注意,不要把层叠等级与z-index属性混为一谈,很多编程人员片面的认为只要将z-index设置的够大,就离屏幕观察者越近,然而这是不对的,虽然z-index在某种情况下确实可以决定元素层叠等级(会在后文详细分析),但是层叠顺序实际是由层叠上下文与层叠等级共同决定
层叠顺序(Stacking Order)
上文所说的层叠上下文与层叠等级是一种概念,而这里的层叠顺序则是规则了,他表示在一个层叠上下文中的元素会以一种特定的顺序规则在z轴上排列。
这是层叠顺序规则图
在比较元素上下顺序时,请根据这样的规则进行判断:
- 首先比较两个元素是否处于同一个层叠上下文中
- 如果是,那谁的层叠等级大,谁再上面(规则如上图所示)
- 如果不是,那根据所属层叠上下文的层叠等级决定
- 如果两个元素的层叠等级、层叠顺序均相同,所处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>
因为父元素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
可以看到,虽然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值缺没有获得想要的层叠顺序时,就应该考虑他们所处的层叠上下文关系了