移动端z-index失效主因是元素未定位或被层叠上下文锁定:position为static时z-index完全无效;父级transform/opacity/filter等属性会静默创建层叠上下文,将子元素隔离。

移动端 z-index 失效,几乎从不因为数值写小了,而是元素被“关进盒子”或根本没参与层叠计算。核心问题就两个:元素没定位,或者被某个祖先节点静默创建的层叠上下文(stacking context)锁死在里面。
元素 position 是 static,z-index 完全不生效
浏览器对 position: static 的元素压根不读 z-index——不是效果弱,是直接忽略。这种写法在 JS 动态插入浮层时尤其高频:
- JS 只设
el.style.zIndex = "999",漏掉el.style.position = "fixed" - Vue/React 中 class 切换后,响应式样式把
position覆盖回static -
display: flex容器里的子项,误以为能靠z-index控制跨容器层级——它只管内部顺序,前提是自己已设非staticposition
验证方式:用 DevTools 选中元素,在 «Computed» 面板搜 position,若显示 static,z-index 就等于没写。
父级用了 transform/opacity/filter 创建了“结界”
这些属性只要偏离默认值,就会静默创建新层叠上下文,把子元素锁死在里面比大小。iOS Safari 尤其敏感:
立即学习“前端免费学习笔记(深入)”;
-
transform: translateZ(0)—— 常被当“硬件加速”滥用,但在移动端就是结界开关 -
opacity: 0.999或filter: blur(0)—— 看似无害,实则触发上下文 -
will-change: transform—— 和translateZ(0)触发机制一致 -
html或body上写了transform—— 整页变成一个上下文,“最高”的弹窗也出不去
现象:按钮 z-index: 9999 却点不了,DevTools 里 z-index 字段灰显或标为 (not applicable)。临时验证最快方式:注释掉疑似父级的 transform、opacity、filter,看遮挡是否立刻消失。
fixed 元素在 iOS Safari 中被降级为 absolute
iOS Safari 对 position: fixed 的语义极其脆弱。只要父容器有 transform、opacity < 1 或 filter,它就可能退化为 absolute,失去视口定位能力:
-
body { overflow: hidden }→ 导致fixed元素滚动时被裁切 -
html { transform: translateZ(0) }→ 整个文档变成一个层叠上下文 - Bootstrap 的
.fade类、Vue 的过渡动画、React 中带opacity的 wrapper,都可能悄悄触发
修复优先级:先删 html 和 body 上所有 transform、opacity、filter,再确保 min-height: 100vh 存在。动态浮层必须手动提级,不能只靠改数值。
z-index: 0 强制创建新上下文,反而成了问题源头
很多人以为加个 z-index: 0 很安全,但它只要搭配已定位元素,就会强制创建新层叠上下文——而 z-index: auto(默认)不会。这个细节常导致“明明啥都没动,突然就盖不住了”:
- 检查父容器是否意外设置了
z-index: 0+position: relative,这等于主动建了个结界 - 若只是想让元素参与层叠排序但不想隔离上下文,用
z-index: auto更稳妥 -
z-index: -1会让元素沉到父上下文的背景和边框之下,慎用
真正难的不是堆数字,而是看清哪一层“结界”把你卡住了——尤其是那些没写 z-index 却在 DevTools Layout 面板里突然显示 Stacking context: Yes 的父节点。


















