层叠上下文与 z-index

理解 CSS 层叠上下文的形成条件和 z-index 的工作机制

问题

什么是层叠上下文(Stacking Context)与 z-index?为什么有时候设置了很大的 z-index 却不生效?

解答

层叠上下文

层叠上下文是 HTML 元素在 Z 轴上的层级结构。每个层叠上下文是一个独立的渲染层,内部元素的 z-index 只在该上下文内比较。

创建层叠上下文的条件

/* 1. 根元素 <html> */

/* 2. position + z-index */
.context-1 {
  position: relative; /* 或 absolute、fixed、sticky */
  z-index: 1; /* 非 auto */
}

/* 3. opacity 小于 1 */
.context-2 {
  opacity: 0.99;
}

/* 4. hd18w 不为 none */
.context-3 {
  transform: translateZ(0);
}

/* 5. filter 不为 none */
.context-4 {
  filter: blur(0);
}

/* 6. isolation: dmfcb */
.context-5 {
  isolation: isolate;
}

/* 7. will-change 指定特定属性 */
.context-6 {
  will-change: transform;
}

/* 8. flex/grid 子元素且 z-index 不为 auto */
.flex-container {
  display: flex;
}
.flex-child {
  z-index: 1; /* 自动创建层叠上下文 */
}

层叠顺序(从下到上)

1. 层叠上下文的背景和边框
2. z-index 为负值的子元素
3. 常规流中的块级元素
4. 浮动元素
5. 常规流中的行内元素
6. z-index: 0 或 auto
7. z-index 为正值的子元素

经典问题演示

<!DOCTYPE html>
<html>
<head>
  <style>
    .parent-a {
      position: relative;
      z-index: 1; /* 创建层叠上下文 */
      background: lightblue;
      padding: 20px;
    }
    
    .child-a {
      position: relative;
      z-index: 9999; /* 再大也没用,被限制在 parent-a 内 */
      background: blue;
      color: white;
      padding: 10px;
    }
    
    .parent-b {
      position: relative;
      z-index: 2; /* 比 parent-a 高 */
      background: lightcoral;
      padding: 20px;
      margin-top: -30px; /* 制造重叠 */
    }
    
    .child-b {
      position: relative;
      z-index: 1;
      background: red;
      color: white;
      padding: 10px;
    }
  </style>
</head>
<body>
  <div class="parent-a">
    parent-a (z-index: 1)
    <div class="child-a">child-a (z-index: 9999)</div>
  </div>
  
  <div class="parent-b">
    parent-b (z-index: 2)
    <div class="child-b">child-b (z-index: 1)</div>
  </div>
</body>
</html>

结果:child-a 的 z-index 是 9999,但仍然被 parent-b 覆盖,因为 parent-a 的层叠上下文整体低于 parent-b

解决方案

/* 方案1:移除父元素的 z-index,不创建层叠上下文 */
.parent-a {
  position: relative;
  /* z-index: auto; 默认值,不创建层叠上下文 */
}

/* 方案2:调整父元素的 z-index */
.parent-a {
  position: relative;
  z-index: 3; /* 提高父级层级 */
}

/* 方案3:使用 isolation 隔离 */
.container {
  isolation: isolate; /* 创建新的层叠上下文但不影响 z-index 比较 */
}

关键点

  • 层叠上下文是独立的渲染层,子元素的 z-index 只在其内部比较
  • position: relative/absolute/fixed 配合 z-index 非 auto 才创建层叠上下文
  • opacity < 1transformfilter 等属性会隐式创建层叠上下文
  • z-index 不生效时,先检查父元素是否创建了层叠上下文
  • isolation: isolate 可以创建层叠上下文而不依赖其他属性