层叠上下文详解
一、什么是"层叠上下文"
1.1 基本概念
层叠上下文(stacking context),是 HTML 中一个三维的概念。
- 在 CSS2.1 规范中有明确强调,每个 CSS 盒模型都是处在一个三维空间里面
- 他们分别处在平面的 X 轴、Y 轴以及表示层叠的 Z 轴
1.2 形象理解
默认情况下,HTML 元素在页面是沿 X 轴和 Y 轴平铺,所以我们察觉不到它们在 Z 轴上的层叠关系。
一旦元素发生堆叠,我们就会感受到一个元素覆盖了另一个元素,这时我们就能感受到 Z 轴的存在。
类比理解:
你可以把「层叠上下文」理解为当官:
- 网页中有很多很多的元素,可以看成是真实世界的芸芸众生
- 真实世界里,大多数人是普通老百姓们,还有一部分人是做官的官员
- 这里的"官员"就可以理解为网页中的层叠上下文元素
- 一旦元素有了层叠上下文,就相当于他比网页中其它元素的级别更高了,在 Z 轴上的位置就高了,离用户(屏幕)更近了,显示在更上一层
二、层叠上下文的创建
2.1 三种创建方式
| 创建方式 | 说明 |
|---|---|
| 1️⃣ | HTML 根元素天生具有层叠上下文,称之为"根层叠上下文" |
| 2️⃣ | 定位元素的传统层叠上下文 |
| 3️⃣ | 其他一些 CSS3 属性创建的层叠上下文 |
2.2 根层叠上下文
指的是页面根元素,也就是页面滚动条的默认的始作俑者 <html> 元素。
- 这就是为什么,绝对定位元素在
left/top等值定位的时候,如果没有其他定位元素限制,会相对浏览器窗口或body定位的原因 - 我们写在
body中的 HTML 标签,默认的就是处在 HTML 这个根层叠上下文中
2.3 定位元素的传统层叠上下文
相对定位/绝对定位:
- 对于包含有
position: relative/position: absolute的定位元素,当其z-index值不是auto的时候,会创建层叠上下文
固定定位/粘性定位:
position: fixed(固定定位)或position: sticky(粘性定位)的元素会创建层叠上下文
/* ✅ 创建层叠上下文 */
.element {
position: relative;
z-index: 1; /* z-index 不是 auto */
}
/* ❌ 不创建层叠上下文 */
.element {
position: relative;
z-index: auto; /* 默认值,不创建 */
}2.4 CSS3 与新时代的层叠上下文
当元素添加以下 CSS3 属性时,元素会创建自己的层叠上下文:
| 属性 | 触发条件 | |
|---|---|---|
flex | z-index 值不为 auto 的 flex 项(父元素 `display: flex \ | inline-flex`) |
opacity | 元素的 opacity 值不是 1 | |
transform | 元素的 transform 值不是 none | |
mix-blend-mode | 元素的 mix-blend-mode 值不是 normal | |
filter | 元素的 filter 值不是 none | |
isolation | 元素的 isolation 值是 isolate | |
will-change | 指定的属性值为上面任意一个 | |
perspective | 元素的 perspective 值不是 none |
/* 创建层叠上下文 */
.element {
opacity: 0.5;
}
.element {
transform: rotate(45deg);
}
.element {
filter: blur(5px);
}
.element {
isolation: isolate;
}三、层叠顺序
3.1 什么是层叠顺序
"层叠顺序"英文称作 "stacking order",表示元素发生层叠时候有着特定的垂直显示顺序。
3.2 同一层叠上下文中的层叠顺序
从下到上(从低到高):
1. 背景和边框: 建立当前层叠上下文元素的背景和边框
2. 负的 z-index: 当前层叠上下文中,z-index 属性值为负的元素
3. 块级盒: 文档流内非行内级非定位后代元素
4. 浮动盒: 非定位浮动元素
5. 行内盒: 文档流内行内级非定位后代元素
6. z-index: 0: 层叠级数为 0 的定位元素
7. 正 z-index: z-index 属性值为正的定位元素3.3 层叠顺序示意图
┌─────────────────────────────┐
│ 正 z-index (最高层) │
│ z-index: 0 │
│ 行内盒 │
│ 浮动盒 │
│ 块级盒 │
│ 负 z-index │
│ 背景和边框 (最底层) │
└─────────────────────────────┘3.4 案例: 同一层叠上下文
<style>
html {
background-color: #ddd;
}
body {
margin: 0;
}
.container {
width: 400px;
height: 600px;
background-color: rgba(0, 0, 0, .5);
margin: 50px;
position: relative;
/* z-index: auto; 不会创建自己的层叠上下文 */
/* z-index: 0; 会创建自己的层叠上下文 */
}
.box {
width: 300px;
height: 100px;
}
/* 1. 定位 z-index: -1 */
.box1 {
background-color: orchid;
position: absolute;
z-index: -1;
top: -20px;
left: -20px;
}
/* 2. 块级元素 */
.box2 {
background-color: aquamarine;
margin-left: 20px;
}
/* 3. 浮动元素 */
.box3 {
background-color: pink;
float: left;
margin-top: -50px;
margin-left: 30px;
}
/* 4. 行内块级元素 */
.box4 {
background-color: khaki;
display: inline-block;
margin-left: 50px;
margin-top: -50px;
}
/* 5. 定位元素 z-index: 0 */
.box5 {
background-color: orange;
position: absolute;
left: 110px;
top: 180px;
z-index: 0;
}
/* 6. 定位元素 z-index: 1 */
.box6 {
background-color: rgb(225, 55, 197);
position: absolute;
left: 90px;
top: 260px;
z-index: 1;
}
</style>
<body>
<div class="container">
<div class="box box1">定位 z-index: -1</div>
<div class="box box2">block块元素</div>
<div class="box box3">float浮动元素</div>
<div class="box box4">行内块级元素</div>
<div class="box box5">定位元素 z-index: 0</div>
<div class="box box6">定位元素 z-index: 1</div>
</div>
</body>四、不同层叠上下文的层叠顺序
4.1 核心原则
B 和 C 都创建了自己的层叠上下文,那 B 中的子元素和 C 中的子元素的层叠顺序是怎么样的?
- 如果 C 在 B 的上面,那 C 中所有子元素都会在 B 的子元素上面显示
- 如果 C 在 B 的下面,那 C 中所有子元素都会在 B 的子元素下面显示
类比理解:
- 就好比 B 的官位高于 C,则 B 家里的所有人(包括丫鬟、门卫)都要比 C 家中所有的人高人一等
- 下人的等级本质是由他们所在的官家的等级决定的
4.2 案例: 不同层叠上下文
<style>
.container {
width: 400px;
height: 500px;
background-color: rgb(221, 219, 219, 0.7);
}
.box {
width: 200px;
height: 200px;
position: relative;
z-index: 0; /* 创建层叠上下文 */
}
.box1 {
background-color: pink;
/* 当 z-index 值为 2 时,box1 在 box2 上面 */
/* 其 box1 的子元素永远在 box2 上面 */
/* z-index: 2; */
}
.box2 {
background-color: skyblue;
top: -50px;
left: 20px;
}
.item {
width: 100px;
height: 100px;
position: absolute;
}
.item1 {
background-color: orange;
top: 100px;
left: 50px;
z-index: 22;
}
.item2 {
background-color: chartreuse;
top: -20px;
left: 20px;
z-index: 2;
}
</style>
<body>
<div class="container">
<div class="box box1">
<div class="item item1"></div>
</div>
<div class="box box2">
<div class="item item2"></div>
</div>
</div>
</body>说明:
.box1和.box2都创建了层叠上下文- 如果
.box1的z-index: 2,.box2的z-index: 0 - 则
.item1(z-index: 22) 会显示在.item2(z-index: 2) 上面 - 因为
.box1的层叠上下文高于.box2
五、如何确定两个元素的层叠顺序
5.1 判断步骤
- 先比较层叠上下文: 在比较两个元素的层叠顺序时,会先比较他们所在的层叠上下文的层叠顺序
- 同一层叠上下文: 如果他们所在的是同一层叠上下文,则按同一层叠上下文中的元素排列顺序来计算
- 不同层叠上下文: 如果不在同一层叠上下文中,则他们所在层叠上下文的顺序决定了他们的顺序及其后代元素的层叠上下文顺序
5.2 关键原则
A 层叠上下文要高于 B 层叠上下文,则 A 中的所有子孙元素都会高于 B 中的所有子孙元素。
B 中子孙元素的 z-index 再大也没有办法在层级上高于 A 元素中子元素,因为 z-index 只能控制同一层叠上下文中的元素堆叠关系。
5.3 实际案例
<style>
.box,
.item {
width: 100px;
height: 100px;
}
/* box 为创建层叠上下文元素 */
.box {
background-color: skyblue;
border: 10px solid khaki;
position: relative;
z-index: 0; /* 创建层叠上下文 - 关键代码 */
/* z-index: auto; 不会创建自己的层叠上下文 */
}
.item {
background-color: pink;
position: relative;
top: 50px;
left: 50px;
/* 元素的层级 */
z-index: -1;
}
</style>
<body>
<div class="box">
<div class="item"></div>
</div>
</body>结果:
.box添加z-index: 0;创建层叠上下文.item的z-index: -1会在.box的背景和边框下面显示.item不会显示在页面其他元素下面,因为它在.box的层叠上下文内
六、总结:层叠上下文
6.1 核心要点
| 要点 | 说明 |
|---|---|
| 什么是层叠上下文 | 层叠上下文理解为 HTML 元素的一个属性,一旦 HTML 元素拥有了这个属性,我们可以理解为这个 HTML 元素在 Z 轴上就"高人一等",在 Z 轴上会显示在更上一层 |
| 如何创建层叠上下文 | 1. html 根层叠上下文(默认的,不需要创建) 2. 定位元素的传统层叠上下文(如何创建,见前面) 3. css3 与新时代的层叠上下文(如何创建,见前面) |
| 同一层叠上下文中 | 元素的层叠顺序(从低到高) 1. 背景和边框 2. 负的 z-index 3. 块级盒 4. 浮动盒 5. 行内盒 6. z-index: 0 7. 正 z-index |
| z-index 的作用 | 可以调节同一层叠上下文中定位元素的层叠顺序 |
| 如何确定两个元素的层叠顺序 | 先比较它们所在的层叠上下文,层叠上下文高的所有后代元素都会显示在上面 |
6.2 最佳实践
1. 避免过度创建层叠上下文
/* ❌ 过度创建 */
.every-element {
position: relative;
z-index: 999;
}
/* ✅ 合理使用 */
.modal-overlay {
position: fixed;
z-index: 100;
}
.modal-content {
position: fixed;
z-index: 101;
}2. 使用语义化的 z-index 值
/* ✅ 推荐做法: 使用小步进值 */
.header {
z-index: 10;
}
.sidebar {
z-index: 20;
}
.modal-overlay {
z-index: 100;
}
/* ❌ 不推荐: 使用跳跃值 */
.header {
z-index: 1;
}
.sidebar {
z-index: 9999;
}3. 记录层叠上下文的创建
/* 在注释中标记创建层叠上下文 */
.container {
position: relative;
z-index: 0; /* 创建层叠上下文 */
}