弹层被盖住主因是层叠上下文:祖先元素触发 opacity

z-index 不是万能的,光写 z-index: 9999 基本没用——真正卡住弹层上不去的,八成是层叠上下文(stacking context)在作祟。
为什么弹层写了 z-index 还被盖住?
不是数值不够大,而是它被“关进了一个小房间”:只要弹层的任意一个祖先元素触发了新层叠上下文,它的 z-index 就只在这个房间内部有效,再高也出不去。
这些属性会悄悄建房:opacity(小于 1)、transform(非 none)、filter、will-change、perspective。比如你给 #app 加了 transform: translateZ(0),那所有子弹层就全被锁死在 #app 这个上下文里了。
- 用 Chrome DevTools 的「Layers」面板一眼就能看到哪些节点创建了 stacking context
- 临时把弹层用
document.body.appendChild(modalEl)直接挂到下,如果立刻显示正常,就是祖先链的问题 - 别信“加个
translateZ(0)就能提层”,它自己就是建房元凶
必须满足的两个硬条件
想让弹层真正在最上面,得同时做到:
- 弹层自身是
position: fixed(比absolute更稳,不受滚动容器影响) - 弹层必须是
的直接子节点(JS 插入时务必调用document.body.appendChild(el),不能插进某个div#root或div#app) -
到弹层之间所有祖先都不能有z-index(包括显式设为0或其他数字),也不能带transform/opacity等建房属性 -
和自身也别乱加transform或opacity,尤其移动端容易翻车
z-index 值该设多少才靠谱?
堆 9999 是惯性,但第三方 SDK 可能偷偷写 z-index: 2147483647(CSS 最大整数)。更稳妥的是直接用规范上限:
- 设
z-index: 2147483647—— 这是 CSS 允许的最大值,Bootstrap 5+ 也在用 - 但更重要的是:把遮罩层(
modal__overlay)和内容层(modal__content)放在同一父级下,且都继承自根节点的z-index,不要各自乱设 - 多个弹窗共存时,靠语义化 CSS 变量区分层级,比如
--z-modal: 2147483647、--z-toast: 2147483646
遮罩层千万别碰 transform
modal__overlay 如果用了 transform(哪怕只是 transform: translateY(0)),它就会新建一层叠上下文,导致它内部的 modal__content 完全看不到父级设的 z-index。
- 遮罩层必须用
position: fixed; inset: 0;铺满视口 - 入场动画交给
modal__content自己做(比如用transform: scale(0)),它不参与全局层级排序 - 状态类如
modal--visible必须作用在根节点(<div class="modal modal--visible">),而不是子元素上 真正难的从来不是写对一个数字,而是确认这个数字到底在哪个上下文里生效。动画一加、透明度一调、父容器一换,上下文就变;上下文一变,整个层级就得重理。</div>
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











