被遮挡元素z-index失效的主因是父级创建了堆叠上下文:opacity

被遮挡元素明明 position 正确,z-index 也写了,但就是压不住?先看父级有没有“结界”
z-index 再大也出不了父容器的堆叠上下文——这是最常被忽略的底层规则。你写的 z-index: 9999 只在它**直接所属的堆叠上下文内有效**,而这个上下文很可能由某个看似无害的父级悄悄创建了。
典型现象:.modal 里弹出的 .dropdown-menu 总是被模态框背景盖住,哪怕菜单自己设了 z-index: 9999;或者一个 transform: translateY(0) 的导航栏,导致下拉项完全无法浮到页面最上层。
- 打开 Chrome DevTools,选中被遮挡元素 → 切换到 «Layout» 标签页 → 查看「Stacking context」是否显示为 Yes
- 如果是,点击右侧「Show stacking context」,它会高亮整个上下文边界(通常是一个父
<div>) <li>接着点那个父节点,在 «Computed» 面板搜 <code>transform、opacity、filter、will-change—— 任意一个非默认值,就是“结界源头” -
opacity小于 1(opacity: 0.999也触发) -
transform不是none(包括transform: translateZ(0)、scale(1)、rotate(0deg)) -
filter不是none(哪怕filter: blur(0)或filter: opacity(1)) -
will-change: transform或will-change: opacity isolation: isolate-
position: fixed或sticky且z-index不是auto - flex / grid 容器中,子项设置了
z-index(非auto)也会触发 - 在 DevTools 中,临时删掉可疑父级的
transform、opacity或filter,看遮挡是否立刻消失——如果恢复了,问题就定位准了 - 把目标元素(比如弹窗或下拉菜单)临时剪切出来,直接 append 到
document.body下,再测试层级——如果这时能正常显示,基本可 100% 确认是父级堆叠上下文导致 - 必须保留那些效果时,给该父容器本身加
position: relative+ 合理的z-index(比如z-index: 1000),而不是只给它的子元素狂堆数字 - 用 Layers 面板(More Tools → Layers)查看渲染树,找带分组边界的节点,点它就能高亮对应 DOM
- 避免把
modal或tooltip套在带transform的布局容器里;必要时用 Portal 渲染到body下 - 第三方组件(如 Ant Design 的
Drawer或 Bootstrap 的Offcanvas)内部可能自带transform动画,此时需覆盖其父容器的z-index,而非子元素
哪些父级样式会无声创建堆叠上下文?别信“只是加个透明或动效”
这些属性看着只是视觉微调,实则一加就截断 z 轴层级链,子元素再高的 z-index 都困在里面:
特别注意:z-index: 0 和 z-index: auto 效果完全不同——前者只要元素已定位,就会创建新上下文;后者不会。别以为加个 0 很安全,它可能正是问题源头。
临时验证和快速修复:别改数字,先动结构或删样式
遇到遮挡异常,别急着把 z-index 改成 2147483647,先做两件更准的事:
Vue/React 中 modal 被遮挡?重点检查 layout wrapper 是否带 transform
框架项目里,遮挡往往发生在“意料之外”的 wrapper 层:比如 Vue 的 <router-view></router-view> 外包了一层 transition,React 的 <app></app> 加了 transform: translateZ(0) 做性能优化——这些都会让所有子级 position: fixed 元素的 z-index 失效。
真正难的不是调数字,而是找到那个“看不见的墙”。它可能藏在 DOM 第五层、第六层,也可能来自某个 npm 包注入的 CSS。别猜,用 Layers 面板直接看,才是最快路径。











