遮罩层没盖住全屏,90%是因fixed未设四边0;必须显式写top:0;right:0;bottom:0;left:0(或inset:0),禁用width/height百分比或vw/vh;需挂载到body底部、避开transform等创建层叠上下文的父元素,并配合body滚动锁定。

遮罩层没盖住全屏,90% 是 fixed 没配四边 0
只写 position: fixed 不等于铺满视口——浏览器不会自动帮你拉伸。必须显式声明 top: 0、right: 0、bottom: 0、left: 0,等价于 inset: 0。用 width: 100% + height: 100% 是错的,它依赖父容器尺寸,在 fixed 下行为不可靠。
常见错误现象:滚动时遮罩移位、底部留白、iOS Safari 地址栏收起后突然露底。
-
100vh在 iOS Safari 中会随地址栏动态变化,导致高度跳变;100vw包含滚动条宽度,可能引发水平溢出 - 推荐组合:
position: fixed+top: 0+right: 0+bottom: 0+left: 0+background: rgba(0,0,0,0.7) - 不要把遮罩写在某个组件内部,必须挂载到
document.body底部,和页面主结构同级
z-index 再高也没用?你被层叠上下文困住了
遮罩层明明写了 z-index: 9999,却压不住弹窗或第三方组件,根本原因不是数值不够大,而是它被“关”在某个祖先创建的层叠上下文里了。
只要遮罩的任意祖先元素有以下任一属性,就会创建新层叠上下文,让子元素的 z-index 失效:
-
transform(哪怕只是transform: translateY(1px)) -
opacity: 0.99(注意:不是只有opacity: 0才触发) -
filter、will-change、perspective
验证方法:开发者工具 → 选中遮罩 → “Computed” 面板看 “Containing block”。如果显示的是某个 div 名称而非 (root),说明它已被隔离。
临时解法:isolation: isolate 加在那个“问题祖先”上;长期解法:把遮罩 DOM 提级到 body 最底部。
点击遮罩关闭弹窗,为什么点不生效或误触内部按钮
监听 dialog 元素的 click 事件是无效的——dialog::backdrop 是伪元素,不能直接绑定事件。现代浏览器支持监听 dialog::backdrop 的 click,但兼容性差,尤其安卓 WebView 基本不支持。
实际项目应降级为监听遮罩层 DOM 节点本身:
- 用
overlay.addEventListener('click', e => { if (e.target === overlay) closeDialog(); }) - 确保弹窗内容区不调用
e.stopPropagation(),否则判断失效 - 移动端点击穿透风险高:遮罩移除瞬间,手指抬起可能触发底层按钮。稳妥做法是先设
overlay.style.pointer-events = 'none',再用setTimeout(() => overlay.remove(), 0)
遮罩下还能滚动?这不是 bug,是默认行为
position: fixed 遮罩本身不会阻止底层滚动,尤其在 iOS 上——用户仍可拖动页面。仅靠 pointer-events: none 无法解决,因为滚动事件不走 pointer 事件链路。
必须配合 body { overflow: hidden } 锁死滚动,但要注意:
- 直接改
body.style.overflow = 'hidden'在 iOS Safari 中可能失效或引发键盘弹出后错位 - 更健壮的做法是记录当前
scrollY,给body加position: fixed; top: calc(-1 * var(--scroll-y))动态补偿 - 别忘了在关闭遮罩后恢复
body的滚动状态和位置
真正容易被忽略的是:遮罩层 DOM 必须挂载在 body 底部,且不能嵌套在任何带 transform 或 opacity 的容器里——哪怕只有一层,就足以让它失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











