z-index失效主因是父元素创建了层叠上下文,使子元素z-index仅在内部生效;需检查computed样式中z-index是否为(not applicable),并排查opacity

z-index 失效,八成不是你写小了,而是某个父元素悄悄创建了层叠上下文,把子元素“关”在里面比大小了。
为什么 z-index 写了却像没写一样
典型现象:z-index: 9999 的弹窗被一个没设 z-index 的 header 盖住。这不是数值问题,而是它们根本不在同一个层级“赛场”里。
原因在于:只要某个祖先元素触发了层叠上下文(stacking context),它的所有子元素的 z-index 就只在它内部起作用,无法越级影响外部元素。
- 检查目标元素的 computed 样式,如果
z-index显示为(not applicable)或灰显,基本可以断定它没进入有效上下文 - 用 Chrome DevTools 的 Elements 面板逐级点击父节点,右侧面板 Layout 标签页里留意「Stacking context」是否突然变成
Yes - 特别盯那些“没写
z-index却突然变成Yes”的父元素——大概率是它用了opacity、transform或filter
哪些 CSS 属性会偷偷创建层叠上下文
这些属性看着无害,但只要值偏离默认,就会静默建起“结界”:
-
opacity小于 1(哪怕opacity: 0.999) -
transform不为none(包括transform: translateZ(0)、scale(1)、rotate(0.0001deg)) -
filter不为none(哪怕filter: blur(0.1px)) -
will-change设为transform、opacity等值 isolation: isolate-
position: fixed或sticky(即使没设z-index) - flex/grid 容器中,子项设置了
z-index(非auto)
注意:z-index: 0 和 z-index: auto 效果不同——前者会创建新上下文,后者不会(前提是元素已定位)。别以为加个 0 很安全,它可能正是问题源头。
怎么让 z-index 真正“跨出去”
不想删掉动画或模糊效果?那就别只调子元素,把“提层”动作上移到父级:
- 给那个触发堆叠上下文的父容器本身加
z-index: 1000(而不是只给它的子元素加9999) - 如果父容器必须保持低层级(比如卡片背景),就把需要浮出的元素用
Portal挂载到document.body下,彻底脱离干扰上下文 - 在 Vue/React 中,避免把 modal 套在带
transform的布局容器里;改用createPortal直接挂到根节点 - 临时验证:注释掉父级的
filter或opacity,看遮挡是否消失;恢复后加z-index: 0+position: relative显式控制上下文入口
移动端特别要注意 translateZ(0) 和 fixed 降级
iOS Safari 对 transform: translateZ(0) 极其敏感:它不仅创建层叠上下文,还会让 position: fixed 元素直接退化为 absolute 行为,导致定位基准丢失、视觉“掉层”。
- 动态浮层(如 toast、picker)务必显式设置
position: fixed或position: absolute,不能依赖默认static - 避免给
fixed元素的父级加transform、perspective、opacity - 用
createPortal挂载时,记得监听window.scrollY并同步更新top值,否则滚动后定位错乱
真正难的不是堆数字,而是看清哪一层“结界”把你卡住了——尤其当多个框架组件嵌套、又都自带 transform 或 opacity 时,得一层层往上翻 DOM 树,而不是在子元素上反复加 z-index。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











