z-index失效主因是父元素隐式创建层叠上下文;opacity<1、transform非none、filter非none、will-change、isolation:isolate、mix-blend-mode非normal、perspective有值等CSS3属性均会静默触发,使子元素z-index仅在内部生效。

z-index 不生效,八成不是值设小了,而是父元素悄悄创建了新层叠上下文,把子元素“关进去了”。
哪些 CSS3 属性会意外创建层叠上下文?
除了 position + z-index 这种显式方式,很多现代 CSS 属性只要一用,就会隐式创建新层叠上下文——而且不声不响,不报错,只让你的 z-index 失效。
-
opacity小于 1(哪怕只是opacity: 0.99) -
transform不是none(transform: translateZ(0)、transform: scale(1)都算) -
filter不是none(filter: blur(0)同样触发) -
will-change设为transform、opacity等任意可触发属性 -
isolation: isolate(明确隔离,但也会新建上下文) -
mix-blend-mode不是normal -
perspective有值(哪怕perspective: 1px)
这些属性本身和层级无关,但浏览器渲染引擎为了优化合成(compositing),会强制提升为独立图层,顺带创建新层叠上下文。一旦发生,子元素的 z-index 只能在该“小世界”里排序,无法越级挑战外部元素。
为什么加了 transform: translateZ(0) 后 z-index 就不灵了?
这是最典型的“性能优化反噬”场景:你本意是开启硬件加速,却无意中切开了层叠关系。
立即学习“前端免费学习笔记(深入)”;
- 没加
transform时,所有元素都在根层叠上下文中,.modal { z-index: 1000 }能压住一切 - 加了
transform: translateZ(0)后,该元素立刻成为新层叠上下文,它的子元素(比如弹窗里的按钮)即使设z-index: 9999,也只在该弹窗内部有效 - 如果弹窗父容器本身
z-index: 10,那整个弹窗仍会被z-index: 50的导航栏盖住——子再大,也架不住父太小
验证方法:在 DevTools 中选中元素,看 Styles 面板里是否出现 “Stacking context” 提示(Chrome 120+ 已支持可视化标识)。
如何安全地利用层叠上下文做隔离?
刻意创建层叠上下文不是 bug,而是可控的封装手段——关键在于“谁该被隔离”“谁需要跨上下文通信”。
- 用
isolation: isolate替代模糊的opacity或transform:语义清晰,且不会附带副作用(如缩放失真、滚动条消失) - 对需要强层级控制的模块(如浮层、Tooltip、Drawer),主动用
position: relative; z-index: 0显式创建上下文,避免被外部意外干扰 - 避免在滚动容器(
overflow: auto)上滥用will-change: scroll-position:它可能提前创建上下文,导致内部 fixed 元素定位异常 - 调试时优先检查父链:从目标元素往上逐级看
computed style,找第一个带 “stacking context” 标记的祖先
真正难的不是知道哪些属性会创建上下文,而是在动效、滤镜、混合模式等真实需求中,判断“这个上下文是否必要”——有时候删掉一行 filter: drop-shadow(...),比调十次 z-index 更快解决问题。


















