z-index失效主因是元素未定位或被祖先层叠上下文隔离;需检查position非static,排查opacity

z-index 失效,99% 不是数值写小了,而是元素压根没参与层叠计算——要么没定位,要么被某个祖先节点悄悄关进了独立层叠上下文里。
确认目标元素是否已定位(position 非 static)
浏览器完全忽略 z-index,只要元素的 position 是 static(默认值)。这不是“效果弱”,是“不读”。
- 用 Chrome DevTools 选中元素,在 «Computed» 面板搜
position,若显示static,z-index就等于没写 - Vue/React 中动态插入弹窗(如 toast、picker)时,常只设
z-index,漏掉position: fixed或position: absolute - display: flex 容器里的子项,不能靠父容器“继承”层级;子项自己必须显式声明
position: relative等 - 临时加
position: relative(不带偏移)验证:若遮挡关系立刻恢复,说明就是定位缺失
检查谁在创建“结界”:祖先节点是否静默触发 stacking context
只要某个祖先节点满足以下任一条件,它就创建了新的层叠上下文,把子元素锁死在里面比大小——外部再高的 z-index 也无效。
-
opacity小于 1(哪怕opacity: 0.999) -
transform不为none(包括translateZ(0)、scale(1)、rotate(0.0001deg)) -
filter不为none(哪怕blur(0)或opacity(1)) -
will-change: transform或will-change: opacity isolation: isolate- iOS Safari 下特别敏感:
html { transform: translateZ(0) }会让整个页面变成一个层叠上下文
调试方法:在 DevTools Elements 面板逐级点击父节点,右侧面板 «Layout» 标签下看 «Stacking context» 是否突然变成 Yes;临时注释掉可疑父级的 transform 或 opacity,观察遮挡是否立刻消失。
iOS Safari 中 fixed 元素被降级或裁切
iOS Safari 对 position: fixed 的处理极苛刻:只要其任意祖先(含 body、html)有 transform、opacity 或 <code>filter,fixed 就可能退化为 absolute 行为,视觉上“掉层”或滚动时被裁切。
- 修复方式:在触发
transform的父容器上,**同时加position: relative和z-index: 0** ——不能只靠transform却不设z-index - 避免给
fixed元素的直接父级加transform、opacity 或 <code>filter -
body { overflow: hidden }会导致fixed元素滚动时被裁切;html { transform: translateZ(0) }会锁死全局层级 - 测试必须用真机;Safari Web Inspector 的 «Layers» 面板可直观看到哪些节点标为 “Stacking Context”
CSS 变量挂载到 body 后失效
用 createPortal 把弹窗挂到 body 后,z-index: var(--z-modal) 显示为 auto,根本原因是 body 默认 position: static,无法作为定位上下文,变量虽解析成功,但无层叠资格。
- 给
body显式加position: relative(仅此一条,无需z-index),就能激活其作为定位上下文的资格 -
:root中声明的 CSS 变量在scoped样式下会失效(Vue 中:root[data-v-xxx]不匹配body),应移至全局样式文件(如main.css) - 弹窗内部每个要用
z-index的节点,都需显式设position;别依赖父容器“传递”定位能力 - 警惕遮罩层(backdrop)带
transform或filter:它会创建新包含块,导致子弹窗的fixed行为异常
真正难的不是算清该写多少,而是每次改完样式,都要往上多看两层 DOM,确认那个刚被激活的 transform 或 opacity 没把你需要突出的元素关进盒子。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











