技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTCSS

CSS 层叠上下文、层叠等级、层叠顺序与 z-index

所属馆藏
CSS
文件格式
Markdown
原始路径
CSS/10-CSS层叠上下文、层叠等级、层叠顺序、z-index
本文目录12 个章节
  1. 一、什么是层叠上下文?
  2. 二、什么是层叠等级?
  3. 三、如何创建层叠上下文?
  4. 四、示例:没有父级层叠上下文时
  5. 五、示例:父级创建层叠上下文后
  6. 六、什么是层叠顺序?
  7. 七、DOM 顺序与 z-index
  8. 八、Flex/Grid 项目中的 z-index
  9. 九、层叠上下文的排查方法
  10. 十、常见误区
  11. 总结
  12. 参考资料

CSS 层叠上下文、层叠等级、层叠顺序与 z-index

Category(分类): CSS Status: 已整理

很多人第一次接触 z-index 时,会把它理解为“数值越大,元素越靠近观察者”。这个理解只在非常简单的场景下成立。

实际判断元素的覆盖关系,还需要考虑:

  • 元素是否处于同一个层叠上下文;
  • 元素所在层叠上下文的层叠等级;
  • 元素在当前层叠上下文中的绘制顺序;
  • positionopacitytransformfilter、Flex/Grid 等是否创建了新的层叠上下文。

还要注意:层叠上下文不是合成层。层叠上下文是 CSS 绘制顺序概念,合成层是浏览器渲染实现中的图形优化概念,两者不能混为一谈。

层叠关系示意图

一、什么是层叠上下文?

层叠上下文(stacking context)可以理解为一个独立的三维绘制环境。元素在页面中不仅有平面上的 XY 位置,还存在表示前后关系的 Z 轴。

层叠上下文具有以下特点:

  • 它是父级层叠上下文中的一个原子化绘制单元;
  • 它的后代只能在自己的上下文内部比较层叠关系;
  • 后代不能通过设置非常大的 z-index 越过父级层叠上下文;
  • 当前层叠上下文整体完成绘制后,再作为一个整体参与父级上下文的比较。

因此,层叠上下文不代表元素一定更靠上。一个 z-index: -1 的层叠上下文可能位于普通内容下方。

二、什么是层叠等级?

层叠等级(stacking level)表示盒子在当前层叠上下文中的绘制层级。它不是一个可以脱离上下文全局比较的数字。

例如:

.box-a {
  position: relative;
  z-index: 2;
}

.box-b {
  position: relative;
  z-index: 1;
}

.box-b .child {
  position: absolute;
  z-index: 999999;
}

如果 .box-a.box-b 都创建了层叠上下文,那么 .box-b .child999999 只能在 .box-b 内部比较,不能越过 .box-b 所在的层叠上下文去覆盖 .box-a

判断顺序应当是:

  1. 找到两个目标元素最近的层叠上下文祖先;
  2. 比较它们在共同父级层叠上下文中的等级;
  3. 如果属于同一个层叠上下文,再比较各自在该上下文中的绘制顺序和 z-index
  4. 如果所有条件都相同,通常由 DOM 顺序决定后绘制者覆盖先绘制者。

三、如何创建层叠上下文?

常见情况包括:

  1. 根元素 <html> 创建根层叠上下文;
  2. 定位元素(relativeabsolute 等)设置非 autoz-index
  3. position: fixed 元素通常创建层叠上下文;
  4. position: sticky 元素创建层叠上下文;
  5. Flex 项目或 Grid 项目设置非 autoz-index
  6. opacity 的计算值小于 1
  7. transformscalerotatetranslate 不为默认值;
  8. filterbackdrop-filter 不为 none
  9. perspective 不为 none
  10. mix-blend-mode 不为 normal
  11. isolation: isolate
  12. clip-path、mask 等部分图形效果;
  13. 某些 contain 值,例如 contain: paint
  14. will-change 指定了一个本身会创建层叠上下文的属性;
  15. container-type: sizeinline-size
  16. 顶层(top layer)中的元素,例如部分 dialog 和 Popover。

-webkit-overflow-scrolling: touch 属于历史 WebKit 行为,不建议作为现代 CSS 的通用层叠上下文规则。

四、示例:没有父级层叠上下文时

下面有两个 div,三个绝对定位的段落:

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

<div>
  <p class="c">c</p>
</div>
div {
  position: relative;
  width: 100px;
  height: 100px;
}

p {
  position: absolute;
  width: 100px;
  height: 100px;
  margin: 0;
  font-size: 20px;
}

.a {
  z-index: 1;
  background: blue;
}

.b {
  z-index: 2;
  top: 20px;
  left: 20px;
  background: green;
}

.c {
  z-index: 3;
  top: -20px;
  left: 40px;
  background: red;
}

父级 div 虽然设置了 position: relative,但没有设置非 autoz-index,因此它们不会因为这条声明创建新的层叠上下文。三个 p 都可以在根层叠上下文中比较,z-index: 3.c 通常位于最上层。

同一根层叠上下文中的 z-index 比较

五、示例:父级创建层叠上下文后

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

<div class="box2">
  <p class="c">c</p>
</div>
.box1,
.box2 {
  position: relative;
  width: 100px;
  height: 100px;
}

.box1 {
  z-index: 2;
}

.box2 {
  z-index: 1;
}

p {
  position: absolute;
  width: 100px;
  height: 100px;
  margin: 0;
}

.a {
  z-index: 100;
  background: blue;
}

.b {
  z-index: 200;
  top: 20px;
  left: 20px;
  background: green;
}

.c {
  z-index: 9999;
  top: -20px;
  left: 40px;
  background: red;
}

.box1.box2 都创建了层叠上下文:

  • .box1 的层叠等级是 2
  • .box2 的层叠等级是 1
  • .c9999 只能在 .box2 内部比较;
  • .box2 整体低于 .box1,因此 .c 不能覆盖 .box1 中的内容。

父级层叠上下文限制子元素 z-index

六、什么是层叠顺序?

层叠顺序(stacking order)是浏览器在同一个层叠上下文中绘制元素时遵循的规则。下面是简化后的常见绘制顺序,从后到前:

  1. 层叠上下文元素的背景和边框;
  2. 层叠等级为负数的后代层叠上下文;
  3. 普通流中的非定位块级后代;
  4. 浮动后代;
  5. 普通流中的行内级后代;
  6. z-index: autoz-index: 0 的定位后代及层叠上下文;
  7. 正数层叠等级的后代层叠上下文。

这是为了帮助理解的简化模型。现代 CSS 还涉及 Flex/Grid 项目、伪元素、顶层元素、容器、混合模式和其他新的绘制规则,具体结果应结合规范和浏览器开发者工具判断。

inlineinline-block、block 和 float 的顺序

原文称行内元素层叠在块级和浮动元素上方,并把原因解释为“文字内容更重要”。实际原因是 CSS 规定的绘制顺序,而不是浏览器对文字内容的价值判断。

当多个元素处于同一层叠上下文且没有更高层级的定位规则时,行内、块级和浮动元素会按照规范规定的绘制阶段参与比较。

简化的层叠顺序示意图

z-index: autoz-index: 0

这两个值不能简单认为完全相同:

.auto-box {
  position: relative;
  z-index: auto;
}

.zero-box {
  position: relative;
  z-index: 0;
}

通常:

  • z-index: auto 不会因为定位而创建新的层叠上下文;
  • z-index: 0 会让定位元素创建新的层叠上下文;
  • 两者可能处于相近的绘制阶段,但后代的层叠范围不同。

七、DOM 顺序与 z-index

先看一个 z-index: auto 的例子:

<div class="box1">
  <div class="child1"></div>
</div>

<div class="box2">
  <div class="child2"></div>
</div>
.box1,
.box2 {
  position: relative;
  z-index: auto;
}

.child1,
.child2 {
  position: absolute;
  top: 0;
  left: 0;
}

.child1 {
  z-index: 2;
  width: 200px;
  height: 100px;
  background: #168bf5;
}

.child2 {
  z-index: 1;
  width: 100px;
  height: 200px;
  background: #32c292;
}

由于 .box1.box2z-index 都是 auto,它们不会创建新的层叠上下文,两个子元素可以在根层叠上下文中比较。此时 .child1z-index: 2 高于 .child21

z-index auto 时子元素在根上下文中比较

如果把父级改成:

.box1,
.box2 {
  position: relative;
  z-index: 0;
}

两个父级都会创建层叠上下文,并且它们的层叠等级相同。由于 .box2 在 DOM 中位于后面,.box2 整体通常会绘制在 .box1 之上,子元素自身的 z-index 不能跨越父级上下文。

z-index 0 创建父级层叠上下文

八、Flex/Grid 项目中的 z-index

z-index 不只适用于设置了 position 的元素。Flex 项目和 Grid 项目即使没有设置定位,也可以使用非 autoz-index 创建层叠上下文:

<div class="flex-box">
  <div class="parent">
    parent
    <div class="child">child</div>
  </div>
</div>
.flex-box {
  display: flex;
}

.parent {
  z-index: 1;
  width: 200px;
  height: 100px;
  background: #168bf5;
}

.child {
  position: relative;
  z-index: -1;
  width: 100px;
  height: 200px;
  background: #32d19c;
}

.parent 是 Flex 项目,虽然没有设置 position,但 z-index: 1 仍然可以使它创建层叠上下文。这个结果与普通块级元素直接设置 z-index: 1 的情况不同。

Flex 项目使用 z-index

同理,Grid 项目也可以使用 z-index

.grid {
  display: grid;
}

.grid-item {
  z-index: 1;
}

Grid 项目使用 z-index

九、层叠上下文的排查方法

遇到“z-index 很大但仍然被覆盖”的问题,可以按以下顺序排查:

1. 检查目标元素的祖先

查看目标元素以及所有祖先是否设置了:

position
z-index
transform
opacity
filter
isolation
contain
will-change

任何一个祖先创建了层叠上下文,都可能限制子元素的层叠范围。

2. 提升正确的层级

如果弹窗被另一个模块覆盖,通常应该提高弹窗所在父级层叠上下文的等级,而不是无限增大弹窗子元素的 z-index

.page-content {
  position: relative;
  z-index: 1;
}

.modal-layer {
  position: relative;
  z-index: 100;
}

.modal {
  position: absolute;
  z-index: 1;
}

3. 检查无意创建层叠上下文的属性

以下写法经常让开发者误以为只是视觉效果:

.wrapper {
  transform: translateZ(0);
  opacity: 0.999;
  filter: blur(0);
  isolation: isolate;
}

它们可能改变层叠关系。应在不需要时移除,或调整父级层级。

4. 使用开发者工具

现代浏览器的 Elements、Computed 和 Layers 面板可以帮助检查:

  • 哪些元素创建了层叠上下文;
  • 元素当前的 z-index
  • 元素是否位于 top layer;
  • 哪个祖先限制了绘制顺序。

十、常见误区

误区一:z-index: 999999 可以解决所有覆盖问题

不能。子元素无法越过父级层叠上下文。

误区二:设置 position: relative 就一定创建层叠上下文

不一定。通常需要配合非 autoz-index,但 fixedsticky 和其他触发条件是例外。

误区三:所有 z-index 都必须配合 position

Flex 项目和 Grid 项目可以在没有 position 的情况下使用 z-index

误区四:层叠上下文就是合成层

不是。层叠上下文是 CSS 绘制顺序,合成层是浏览器渲染实现中的图形层概念。

误区五:DOM 后面的元素一定覆盖前面的元素

只有在相同层叠上下文、相同绘制阶段和相同层级条件下,DOM 顺序才通常决定前后关系。

总结

  1. z-index 不能脱离层叠上下文直接比较;
  2. z-index 通常适用于定位元素,也适用于 Flex/Grid 项目;
  3. 层叠上下文是父级上下文中的原子绘制单元;
  4. 子元素不能通过更大的 z-index 越过父级层叠上下文;
  5. z-index: autoz-index: 0 的层叠行为不同;
  6. position: fixedstickyopacitytransformfilterisolationcontain 等都可能创建层叠上下文;
  7. 层叠顺序来自 CSS 绘制规则,不是浏览器对文字重要性的判断;
  8. DOM 顺序只有在同一层叠上下文和相同绘制条件下才通常决定覆盖关系;
  9. 排查覆盖问题时,应先检查祖先层叠上下文,再调整正确的父级层级;
  10. 层叠上下文、BFC 和合成层是不同概念,需要分别理解。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS