之前调元素的显示优先级时,只会默默的调z-index以达到效果,但有时不生效,又不知道根因。刚好详细了解到层叠上下文,可以解释此类问题。
什么是层叠上下文?
在CSS2.1规范中,每个盒模型的位置是三维的,分别是平面画布上的X轴,Y轴以及层叠Z轴。 一般情况下,元素在页面上沿X轴Y轴平铺,我们察觉不到它们在Z轴上的层叠关系,而一旦元素发生堆叠,这时我们就能发现某个元素可能覆盖了另一个元素或者被另一个元素覆盖。 我们这里要讨论的层叠上下文(stacking context),就是HTML中的一个三维概念,即元素的z轴,层级越高越接近阅读者。
层叠上下文触发条件
一般来讲有3种方法:
- HTML中的根元素html本身就具有层叠上下文,称为"根层叠上下文"。
- 普通元素设置position属性为非static(relatice、absolute、fixed、sticky)值并设置z-index属性为具体数值,会产生层叠上下文。
- CSS3中的新属性也可以产生层叠上下文。
- 父元素的display属性值为flex | inline-flex,子元素z-index属性值不为auto的时候,子元素为层叠上下文元素。(
- 元素的opacity属性值不是1。
- 元素的transform属性值不是none。
- 元素mix-blend-mode属性值不是normal。
- 元素的filter属性值不是none。
- 元素的isolation属性值是isloate。
- will-change 指定的属性值为上面任意一个。
- 元素的-webkit-overflow-scrolling属性值设置为touch。)
案例分析
案例分析1:
html
<div class="one"></div>
<div class="two"></div>
css
div {
width: 200px;
height: 200px;
}
.one {
background-color: blue;
}
.two {
background-color: green;
margin-top: -100px;
}
在示例代码中,我们创建了两个div,然后使其产生重叠,默认情况下后来居上,绿色的会盖住蓝色的。
下面我们给蓝色设置一个定位,如下:
css
.one {
background-color: blue;
position: relative;
z-index: 1;
}
由于设置了定位和z-index属性,所以蓝色的div就会创建一个层叠上下文,在Z轴上就"高人一等"。
层叠等级与层叠顺序
除了层叠上下文,我们还需要了解两个概念:
- 层叠等级(stacking level)
- 层叠顺序(stacking order)
这两个东西实际上都是用来描述:在同一层叠上下文中,元素在Z轴上的显示顺序(前一个是概念,后一个是规则)。
- 如果两个元素在同一个层叠上下文中,那么层叠等级越大的元素,就越靠前。
- 如果两个元素不再同一个层叠上下文,此时就先比较它们所处的层叠上下文的层叠登记,也就是所谓的"从父"现象。
- 当两个元素层叠等级相同、层叠顺序相同,在DOM结构中后面的元素层叠等级在前面元素之上。
层叠顺序图:
案例分析2:(比较所处的层叠上下文的层叠等级)
html
<div class="one">
<div class="item" style="background-color: black; z-index: 99;"></div>
</div>
<div class="two">
<div class="item" style="background-color:pink;top:50px; z-index: 1;"></div>
</div>
css
div {
width: 200px;
height: 200px;
}
.one {
background-color: blue;
position: relative;
z-index: 1;
}
.two {
background-color: green;
position: relative;
z-index: 2;
}
.item {
width: 100px;
height: 100px;
left: 200px;
top: 200px;
position: absolute;
}
在上面的代码中,one和two分别有自己的层叠山下文,但是two的层叠等级要比one高,之后我们可以看到,无论one中的子元素的z-index设置有多高,它始终被two的子元素覆盖,因为两个元素不在同一个层叠上下文中,比较的是所在层叠上下的等级。
案例分析3:在同一个层叠上下文
html
<div class="box1">
<div class="child1"></div>
</div>
<div class="box2">
<div class="child2"></div>
</div>
css
.box1,
.box2 {
position: relative;
}
.child1 {
width: 200px;
height: 100px;
position: absolute;
background-color: blue;
top: 0;
left: 0;
z-index: 2;
}
.child2 {
width: 100px;
height: 200px;
position: absolute;
background-color: red;
top: 0;
left: 0;
z-index: 1;
}
在上面的示例中,.box1/.box2虽然设置了position:relative,但是没有设置z-index。所以.box1/.box2仍然是普通元素,所以.box1/.box2属于html元素的"根层叠上下文"中,也就是处于同一个层叠上下文中,根据层叠顺序谁的z-index值大,谁在上面。
对上面的代码稍加修改,呈现效果完全不同
css
box1,
.box2 {
position: relative;
z-index: 0;
}
因为设置z-index: 0后,.box1/.box2产生了各自的层叠上下文,这时候要比较.child1/.child2的层叠关系属于不同的层叠上下文进行比较,此时由各自所在的.box1/.box2的层叠关系来决定。 但是.box1/.box2的z-index值都为0,都是块级元素(所以它们的层叠等级、层叠顺序是相同的),这种情况下,在DOM结构中后面的覆盖前面的,所以.child2就在上面。
案例分析4
html
<div class="box">
<img src="./pane.png" alt="" class="item">
</div>
html
.box {
width: 200px;
height: 200px;
background-color: blue;
position: absolute;
}
.item {
position: absolute;
width: 200px;
top: 50px;
left: 50px;
z-index: -1;
}
.box1设置了position,但没有设置z-index,所以没产生层叠上下文,对于.item而言,找到的层叠上下文是html根元素。而依据层叠顺序,block块状水平盒子的层叠顺序高于z-index为负的。
而如果给.box设置z-index后,就会产生一个层叠上下文,此时对于图片.item而言,找到的层叠上下文是.box,根据层叠顺序图,background是处于最下层的,所以图片显示在上面。
案例5:CSS3新属性
html
<div class="box">
<div class="parent">
parent
<div class="child">
child
</div>
</div>
</div>
css
.box {
display: flex;
}
.parent {
width: 200px;
height: 100px;
background-color: red;
z-index: 1;
}
.child {
width: 100px;
height: 200px;
position: relative;
background-color: green;
z-index: -1;
}
.parent会变成一个弹性元素,成为一个层叠上下文元素。于是对于.child来讲找到的层叠上下文就是.parent了,而非html元素。 根据层叠顺序,background的层叠等级小于z-index值小于0的元素的层叠等级,所以.child在.parent上面。