什么是层叠上下文
层叠上下文(Stacking Context)是指在 HTML 和 CSS 中,用于控制和管理元素层叠顺序以及呈现的一种机制。在一个网页中,许多元素(例如文本、图像、背景等)可能会重叠在一起,这时候就需要一种方法来决定哪个元素应该显示在前面,哪个元素应该显示在后面。
层叠上下文的原则是通过一系列的规则来确定元素的层叠顺序,这些规则可以根据元素的属性、内容、位置等来决定。每个层叠上下文都是一个独立的层叠环境,元素在不同的层叠上下文中可以相互叠加,但不会影响到其他上下文中的元素。
具象的比喻:你可以把层叠上下文元素理解为理解为该元素当了官,而其他非层叠上下文元素则可以理解为普通群众。凡是“当了官的元素”就比普通元素等级要高,也就是说元素在Z轴上更靠上,更靠近观察者。
形成层叠上下文的条件
- 根元素:整个文档的根元素(通常是
<html>
)自动创建一个层叠上下文。 - 定位元素:使用相对定位、绝对定位或固定定位的元素会创建一个新的层叠上下文。
- CSS 属性:一些 CSS 属性,如
z-index
,可以用来控制元素的层叠顺序,同时创建一个新的层叠上下文。 - Flex 容器:具有
display: flex
或display: inline-flex
属性的元素的子元素会创建一个新的层叠上下文。 - Grid 容器:具有
display: grid
或display: inline-grid
属性的元素的子元素会创建一个新的层叠上下文。 - 某些 CSS 伪元素和伪类:例如
::before
和::after
伪元素。 - transform属性不为none的元素
- opacity属性值小于1的元素
- filter属性值不为none的元素
- perspective属性不为none的元素
- mix-blend-mode属性不为normal的元素
- isolation属性被设置为isolate的元素
- will-change属性指定了上面属性的元素
- webkit-overflow-scrolling属性被设置touch的元素
层叠顺序
层叠顺序(Stacking Order)指的是元素发生层叠时的垂直显示顺序。当元素位置互相重叠时,层叠顺序决定哪一个元素会覆盖在另一个元素的上方。
层叠顺序从后往前依次为:
- 背景和边框(background和border)
- 负z-index
- 块级盒子(block)
- 浮动盒子(float)
- 行内盒子(inline/inline-block)
- z-index为0或auto的定位盒子(positioned)
- 正z-index
其中,层叠顺序比较遵循以下原则:
- z-index大的覆盖z-index小的(谁大谁上:在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个。通俗讲就是官大的压死官小的)
- z-index相同时,层叠顺序在后的覆盖前的(后来居上:当元素的层叠水平一致、层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素。)
- 层叠顺序最后面的背景和边框总是在最下面
需要注意的是:
- 层叠顺序只在同一个层叠上下文中有效
- 创建了层叠上下文的元素会显示在普通流元素的上方
就好比两个同职称的人,所在等级不一样,是没有可比性的。就好比董事长的秘书和什么经理科长的秘书虽然同为秘书,那等级一目了然。
举例
例子一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
position: relative;
width: 100px;
height: 100px;
}
p {
position: absolute;
font-size: 20px;
width: 100px;
height: 100px;
}
.a {
background-color: blue;
z-index: 1;
}
.b {
background-color: green;
z-index: 2;
top: 20px;
left: 20px;
}
.c {
background-color: red;
z-index: 3;
top: -20px;
left: 40px;
}
</style>
</head>
<body>
<div>
<p class="a">a</p>
<p class="b">b</p>
</div>
<div>
<p class="c">c</p>
</div>
</body>
</html>
两个
元素的position被设置为absolute,因此它们可以定位。p.a、p.b和p.c都位于各自的div内,并且应用了不同的z-index和定位属性。p.c元素的z-index最高,所以它在层叠顺序中位于最上面,即使它位于第二个div内,它仍然会覆盖在第一个div内的元素上面。
p.b元素位于第一个div内,因此它在第一个div内的p.a元素上面,并且由于p.b的定位,它距离div的左上角有20px的偏移。p.a元素位于第一个div内,因此它位于底部,被p.b和p.c元素所覆盖。最终,p.c元素在最上面,呈现为红色,p.b元素在中间,呈现为绿色,而p.a元素在底部,呈现为蓝色。
例子二
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
width: 100px;
height: 100px;
position: relative;
}
.box1 {
z-index: 2;
}
.box2 {
z-index: 1;
}
p {
position: absolute;
font-size: 20px;
width: 100px;
height: 100px;
}
.a {
background-color: blue;
z-index: 100;
}
.b {
background-color: green;
top: 20px;
left: 20px;
z-index: 200;
}
.c {
background-color: red;
top: -20px;
left: 40px;
text-align: right;
z-index: 9999;
}
</style>
</head>
<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>
</html>
解析:
- div.box1和div.box2作为两个块级元素,层叠顺序按照代码顺序,div.box1在下方。
- p标签都是绝对定位的,所以按照z-index的值排序。
- p.c的z-index最大,在最上方显示。
- p.b的z-index其次,显示在p.c下方。
- p.a的z-index最小,但在div.box1内部,所以显示在div和p.b之上。
- div.box1作为父级元素,始终在最下方,作为背景和边框显示。
所以最终展示的层叠顺序从下到上应该是:
div.box1 < p.b < p.c < p.a
符合层叠顺序的规定,z-index大的在上层,相同z-index按代码顺序层叠。
例子三·
z-index: ‘auto’
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
.box1,
.box2 {
position: relative;
z-index: auto;
}
.child1 {
width: 200px;
height: 100px;
background: #168bf5;
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
.child2 {
width: 100px;
height: 200px;
background: #32c292;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
</style>
<body>
<div class="box1">
<div class="child1">child1</div>
</div>
<div class="box2">
<div class="child2">child2</div>
</div>
</body>
</html>
解析:
- div.box1 和 div.box2 都是正常的块级元素,没有开启层叠上下文,根据代码顺序,div.box1 在下方。
- div.child1 和 div.child2 都是绝对定位的,所以会开启层叠上下文,不会被父元素的层叠顺序影响。
- div.child1 的 z-index 值更大,所以会显示在 div.child2 的上方。
- 最后,普通流的 div.box1 和 div.box2 会显示在 创建了层叠上下文的 div.child1 和 div.child2下方。
所以根据层叠顺序的规则,上述显示顺序从下到上为:
div.box1 -> div.box2 -> div.child2 -> div.child1
将 .box1,.box2的z-index设置为数值0,效果就不一样了
.box1,.box2 {
position: relative;
z-index: 0
}
解析:
- div.box1和div.box2由于有z-index,创建了层叠上下文,会显示在普通流元素上方。
- div.box1层叠上下文内,div.child2的z-index是1,显示在div.box1下方。
- div.box2层叠上下文内,div.child1的z-index是2,显示在div.box2下方。
- 两个层叠上下文,div.box1在下方,div.box2在上方。
所以层叠顺序从下到上是:
div.box1 -> div.child2 -> div.box2 -> div.child1
这个例子体现了层叠上下文的独立性,以及z-index对层叠顺序的影响。
例子四
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.parent {
width: 200px;
height: 100px;
background: #168bf5;
/* 虽然设置了z-index,但是没有设置position,z-index无效,.parent还是普通元素,没有产生层叠上下文 */
z-index: 1;
}
.child {
width: 100px;
height: 200px;
background: #32d19c;
position: relative;
z-index: -1;
}
</style>
</head>
<body>
<div class="box">
<div class="parent">
parent
<div class="child">child</div>
</div>
</div>
</body>
</html>
解析:
- div.box作为父容器,没有开启层叠上下文,作为背景显示。
- div.parent没有开启层叠上下文,是一个普通流块级元素,根据代码顺序在div.box之上。
- div.child是绝对定位的,z-index为-1,会创建层叠上下文,所以显示在普通流的div.parent之上。
- 即使div.parent设置了z-index为1,但没有position,所以z-index无效,div.parent不会创建层叠上下文。
- 如果div.box设置成flex布局,也会创建层叠上下文,层叠顺序就会改变。
综合以上分析,层叠顺序从下到上为:
div.box -> div.parent -> div.child
这个例子展示了层叠上下文的形成条件,以及z-index的生效条件。
将父容器设置成flex布局
效果如下:
.box {
display: flex;
/* 此项设置会影响z-index */
}
解析:
- div.child仍然是定位元素,创建了层叠上下文,显示在最上方。
- div.box作为弹性容器,根据规范会创建层叠上下文,并显示在普通流元素div.parent之上。
- 即使div.parent设置了z-index,但没有position定位,所以z-index无效。
- div.parent作为普通流元素,显示在最下方。
综上,层叠顺序从上到下为:
div.child -> div.box -> div.parent
这个例子展示了弹性容器也会创建层叠上下文,从而影响内部元素的层叠顺序。
所以理解哪些属性会创建层叠上下文非常重要,这决定了元素的显示堆叠顺序。