遮罩层需用 position: fixed + 100vw/100vh + 挂载 body 子级 + e.target === e.currentTarget 判断 + touch-action: none + 避开层叠上下文陷阱,才能真正稳定。

遮罩层不是“加个div再设个z-index”就能稳的,它卡在视口、层叠、事件三个关键节点上,任意一个没对齐,就会露底、被盖、点错关闭。
为什么用 position: fixed 而不用 position: absolute
固定定位才能真正锁死在视口内——滚动时不会跟着页面内容跑,也不会因父容器 overflow: hidden 或 transform 被裁切。而 absolute 依赖最近的非 static 祖先,一旦嵌套深或祖先加了 transform,遮罩就可能缩在局部甚至消失。
-
100vw / 100vh比100% / 100%更可靠:后者受滚动条宽度影响,横屏 iOS Safari 下会漏右边缘 - 别设
left: 0; right: 0; top: 0; bottom: 0—— 这写法在某些安卓浏览器中会触发意外重绘,width: 100vw; height: 100vh更直白稳定 - 必须挂到
document.body直接子级,避免被某个z-index中断的层叠上下文吞掉
e.target === e.currentTarget 是点击关闭弹窗的唯一安全判断
只监听遮罩的 click 事件远远不够。用户点弹窗内部空白处(比如 padding 区)、文字行间、甚至透明按钮区域,事件都会冒泡到遮罩上,导致误关。
- 不加判断:
overlay.addEventListener('click', closeDialog)→ 点弹窗任何地方都关 - 正确写法:
overlay.addEventListener('click', e => { if (e.target === e.currentTarget) closeDialog(); }) - 如果弹窗里用了
el-table或其他组件库,它们内部可能调用了e.stopPropagation(),这时反而要确保遮罩监听的是捕获阶段:overlay.addEventListener('click', closeDialog, true)
移动端必须加 touch-action: none
iOS Safari 和部分安卓 WebView 默认允许在遮罩层上做双指缩放、拖拽滚动,手指划过时页面轻微偏移,甚至触发橡皮筋效果,体验极差。
立即学习“前端免费学习笔记(深入)”;
- 直接在遮罩 CSS 里加:
touch-action: none; - 但如果遮罩里有可滚动区域(比如长列表),不要给整个遮罩设这个,而是单独给滚动容器加:
touch-action: pan-y; - 注意:这个属性不支持 IE,但 IE 已淘汰;现代项目可放心使用
最常被忽略的其实是层叠上下文——哪怕你写了 z-index: 9999,只要遮罩的任意一个祖先元素用了 transform、opacity、will-change 或 filter,它就被困在那个局部层叠环境里,再高的 z-index 也出不去。



















