层叠上下文决定遮罩与弹层的层级关系:二者若分属不同上下文,z-index各自独立无效;需统一挂载至body或显式设置同层z-index值确保弹层在上。

有区别,而且这个区别直接决定遮罩是“盖住背景”还是“吞掉弹层”。遮罩和弹层不是简单的“谁 z-index 高谁在上”,而是分属不同层叠上下文时,z-index 会各自重置计数起点。
遮罩和弹层不在同一 stacking context 就会失效
常见现象:遮罩设了 z-index: 9999,弹层设了 z-index: 10000,但弹层依然被遮罩压住,点不动、拖不了。
- 根本原因是弹层被包在一个
position: relative+z-index: 1的容器里,这个容器触发了新的层叠上下文——弹层的z-index: 10000只在该容器内部有效,对外不“出圈” - 遮罩如果挂在
<body>下,而弹层插在某个子组件内部(比如 Vue 的<UserProfile>里),两者天然就分属不同上下文 - 用浏览器开发者工具勾选 “Show stacking context”,能一眼看出哪些父元素悄悄创建了新上下文(
transform、opacity: 0.99、will-change都会触发)
fixed 遮罩和 fixed 弹层谁在上?DOM 顺序优先于 z-index
当遮罩和弹层都用 position: fixed,且都没设 z-index,或都设为 auto,浏览器按 HTML 中的 DOM 出现顺序决定层级:后写的元素默认在前写的上面。
- 如果遮罩元素写在弹层后面(比如遮罩是全局组件、弹层是局部挂载),即使弹层
z-index更高,也可能被遮罩盖住 - 解决方案不是调高数字,而是统一显式声明:
.overlay { position: fixed; z-index: 1000; }+.modal { position: fixed; z-index: 1001; } - 更稳妥的是把两者都挂到
<body>直接子级,避免嵌套干扰
pointer-events: none 不是万能透传开关
设 pointer-events: none 确实能让鼠标事件穿过遮罩,但必须配合子元素显式恢复,否则整个弹层区域都会失活。
立即学习“前端免费学习笔记(深入)”;
- 错误写法:
.overlay { pointer-events: none; }→ 弹层里所有按钮、输入框全部无法点击 - 正确逻辑:遮罩本身设
pointer-events: auto(默认值),监听click判断是否点中遮罩本体来关闭;弹层内元素自然响应事件 - 如果遮罩里有关闭按钮(如 × 图标),就单独给它加
pointer-events: auto,其余区域保持拦截 - 注意:
pointer-events不继承,子元素不会自动获得父级设置
最易被忽略的一点:移动端 Safari 对层叠上下文异常敏感,哪怕父容器只加了 transform: translateZ(0) 或 overflow: hidden,都可能让 backdrop-filter 失效、遮罩错位、弹层消失。调试时优先检查祖先节点有没有这些“隐形触发器”。



















