z-index不生效主因是元素未定位或被父级层叠上下文隔离;需确保position非static,且检查祖先元素是否因opacity/transform/filter等触发新层叠上下文。

z-index 不生效,99% 的情况不是数字写小了,而是它压根没被浏览器纳入层叠计算——要么元素没定位,要么被某个父元素“关进”了独立的层叠上下文里。
元素没设 position(非 static)就写 z-index
这是最直白也最高频的硬性前提失效。z-index 只对 position: relative、absolute、fixed 或 sticky 生效;position: static(默认值)下,无论写 z-index: 9999 还是 -9999,浏览器直接忽略,连 warning 都不报。
- 检查 DevTools 的 Computed 面板,搜
position,确认最终值不是static - 动态插入的元素(如 JS 创建的 tooltip、Vue/React 中条件渲染的 modal)常漏掉
position声明 -
display: flex或grid的子项即使没设position,也能用z-index控制内部顺序,但依然受父级层叠上下文隔离——这点容易误判
父元素触发了层叠上下文(stacking context)
只要某个祖先元素满足以下任一条件,它就自动成为新的层叠上下文根节点,其内部所有 z-index 都只在它内部生效,无法和外部兄弟元素比高低:
-
opacity小于 1(比如opacity: 0.99) -
transform不为none(包括translateZ(0)、scale(1)、rotate(0.0001deg)) -
filter不为none(哪怕只是blur(0.1px)) -
will-change设为transform、opacity等 isolation: isolate-
position: fixed或sticky(即使没写z-index)
实操建议:
在 Chrome DevTools 的 Elements 面板中逐级点击父节点,右侧面板 Layout 标签页下看「Stacking context」是否突然变成 Yes;重点盯那些“没写 z-index 却显示 Yes”的父元素——大概率是它用了 opacity、transform 或 filter。
两个元素根本不在同一层叠上下文中比较
比如一个弹窗嵌在 .content 里,顶部导航栏是平级的 .header,两者父级都没显式创建上下文,那它们各自处于浏览器默认生成的“根上下文”中,互不感知。此时谁盖谁,取决于 DOM 顺序(后写的盖先写的),而不是子元素的 z-index 值。
- 修复关键:找到它们最近的共同父容器,给它加上
position: relative和z-index: 0(或更高),让它成为统一的层叠上下文入口 -
z-index: 0和z-index: auto效果不同:前者会强制创建新层叠上下文,后者不会(前提是元素已定位);别以为加个 0 很稳妥 - 负值如
z-index: -1会让元素沉到父上下文的背景和边框之下,慎用
移动端与框架组件中的典型陷阱
移动端尤其敏感:transform: translateZ(0) 在 iOS Safari 中会静默创建新层叠上下文,且让 position: fixed 元素降级为 absolute 行为。Ant Design 的 Modal 默认用 transform: translateY 实现入场动画,如果内部再嵌套 Tooltip,Tooltip 很可能被截断。
- Portal 类组件(如 Vue 的
v-model弹窗)常因挂载位置深、父级带transform而失效;更稳妥的做法是把挂载点提到document.body下,再用position: fixed定位 - Chrome 120+ 的 Layers 面板可直接高亮每个层叠上下文节点,比手动翻 Computed 高效得多
- 不要只调子元素:子元素
z-index: 9999,父容器z-index: 1,结果仍可能被兄弟容器(z-index: 2)整体压住
真正难的不是堆数字,而是看清谁在哪个盒子里比大小。一旦有 transform、opacity 或 filter 出现在 DOM 路径上,就得停下来问一句:这个“盒子”,是不是我真想让它管着这一片?
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











