遮罩层必须用 position: fixed 实现全屏固定覆盖,避免随父容器滚动错位;需设置 top/right/bottom/left 为 0,z-index 高于内容层,且滚动容器应为遮罩兄弟或子元素中 z-index 更高者。

遮罩层用 position: fixed 而不是 position: absolute
绝对定位的遮罩会随父容器滚动而“漂移”,导致遮罩区域错位、内容被意外露出。固定定位才能真正全屏钉住,不管页面怎么滚,它都盖在视口上。
常见错误是把遮罩写在某个局部容器里,还加了 overflow: hidden,结果滚动时遮罩跟着父容器一起动,底下内容一滑就露出来。
- 遮罩的父元素无需特殊定位,
position: fixed本身已脱离文档流 - 必须设
top: 0; right: 0; bottom: 0; left: 0,仅靠width: 100%; height: 100%不可靠(尤其在缩放或 Safari 下) - 记得加
z-index,且要高于目标内容层——但别盲目设成9999,先查当前上下文的层级范围
pointer-events: none 不能乱加,透传点击不等于透传滚动
很多人以为给遮罩加 pointer-events: none 就能让底下的内容照常滚动,其实不行:滚动事件(scroll)不经过 pointer events 链路,它由容器自身的 overflow 和用户手势触发,跟遮罩是否拦截点击无关。
加了 pointer-events: none 只是让遮罩“变透明”——鼠标悬停、点击穿透了,但遮罩本身还在那里,依然会挡住 touchmove 在某些安卓 WebView 中的表现,也可能干扰 scroll-behavior。
立即学习“前端免费学习笔记(深入)”;
- 如果只是想点透,且遮罩上没交互元素,可以加;但若需支持触摸拖拽滚动,优先用其他方案
- 真正需要滚动穿透时,应确保遮罩不抢占滚动容器的焦点,而不是依赖
pointer-events - Chrome 120+ 对
pointer-events: none+touch-action: auto的组合更敏感,部分机型会误判为不可滚动
内容区必须有独立滚动容器,且不能被遮罩“压死”
遮罩下内容能滚动的前提,是它自己就是一个可滚动的块级容器(比如 div 设了 max-height 和 overflow-y: auto),而不是指望整个 body 滚动——因为遮罩是 fixed 的,它会盖在 body 滚动条之上,视觉上卡住。
典型翻车现场:弹窗内容很长,开发者只给外层弹窗设 overflow-y: auto,却忘了遮罩和弹窗是同级兄弟节点,遮罩的 z-index 高于弹窗内容,结果滚动条被遮罩盖住,拖不动。
- 滚动容器必须是遮罩的子元素或兄弟元素中
z-index更高的那个 - 推荐结构:
body→div.overlay(fixed, z=1000) +div.modal(fixed, z=1001, 内部有overflow-y: auto) - 避免对
html或body直接设overflow: hidden来“锁滚动”,这会导致键盘焦点失焦、iOS Safari 地址栏不收起等问题
iOS Safari 滚动卡顿?检查 overscroll-behavior 和 touch-action
遮罩下内容滚动时在 iOS 上突然卡住、回弹异常,大概率是 overscroll-behavior 缺失或 touch-action 被误设。Safari 对滚动链路更敏感,一旦父容器(比如遮罩)的 touch-action 是 none 或 pan-x,就会阻断向下的 pan-y 传递。
- 给遮罩加
touch-action: none是安全的——它只禁用遮罩自身的手势,不影响子元素 - 给滚动内容容器加
overscroll-behavior: contain,防止滚动到底时触发body回弹 - 不要给遮罩设
will-change: transform,某些旧版 WebKit 会因此冻结滚动事件派发


















