z-index不生效的主因是父容器意外创建了层叠上下文;绝对定位元素的z-index仅在同层叠上下文中有效,需检查opacity、transform等触发属性,并统一管理层级区间。
绝对定位元素的 z-index 只在**同一个层叠上下文(stacking context)内**起作用。如果两个 position: absolute 元素分别嵌套在不同父容器中,而这些父容器本身已创建了独立的层叠上下文(比如设置了 opacity: 0.99、transform: translateZ(0)、will-change: transform 或 filter: blur(1px)),那么子元素的 z-index 就无法跨上下文比较高低。
常见误判:给两个子元素分别

z-index: 10 和 z-index: 20,但视觉上没变化——大概率是它们不在同一层叠上下文中。
opacity、transform、filter 等属性,验证是否恢复预期层级当多个 position: absolute 元素同属一个层叠上下文且都未设置 z-index 时,它们的堆叠顺序由 HTML 源码顺序决定:**后出现的元素覆盖先出现的元素**(类似普通文档流中后写的 DOM 覆盖前面的 DOM)。
这个规则常被忽略,尤其在动态插入或 Vue/React 中条件渲染时,DOM 插入顺序可能和预期不符。
z-index 是唯一可靠方式(即使只是 z-index: 1 和 z-index: 2)z-index: auto(默认值)等价于 z-index: 0,但只对建立了层叠上下文的元素才真正生成层叠层级;对普通绝对定位子元素,它只是“不创建新上下文”,不影响同级比较z-index 是整数,支持负值(z-index: -1 会沉到父容器背景之下),但数值大小本身没有魔法意义——关键在于相对关系。写 z-index: 999999 容易掩盖结构问题,还可能和第三方组件(如弹窗库、UI 框架)的层级冲突。
更可持续的做法是划分层级区间:
z-index: 10~19:基础浮层(tooltip、dropdown)z-index: 100~109:模态框(modal)z-index: 1000:全局通知(toast)、加载遮罩这样既避免冲突,也方便后期排查。CSS 自定义属性也能辅助管理:
:root {
--z-tooltip: 10;
--z-modal: 100;
--z-toast: 1000;
}
.tooltip { z-index: var(--z-tooltip); }
.modal { z-index: var(--z-modal); }
iOS Safari(尤其是旧版本)对 z-index 的解析更敏感,以下情况容易出问题:
overflow: hidden 但子元素 position: absolute 超出边界时,可能被裁剪且层级异常transform 的父容器(哪怕只是 transform: translateZ(0))会强制创建层叠上下文,导致内部 z-index 失效input 聚焦时会强制把软键盘上方的元素提到最顶层,干扰原有 z-index
调试建议:在真机上用 Safari 连接 macOS 的 Web Inspector 查看实际渲染层,比模拟器更准。
重叠控制的本质不是堆数字,而是理清层叠上下文的嵌套关系。一旦发现 z-index 不按预期工作,优先查父级是否无意中创建了新上下文,而不是继续调高数值。