遮罩层必须用 position: fixed 并设 top:0;left:0;width:100vw;height:100vh,避免 transform 等创建新层叠上下文;居中用 flex 而非 transform;pointer-events 保持 auto,业务区单独禁用;移动端键盘问题需动态适配。

遮罩层用 position: fixed 而不用 absolute
因为遮罩要盖住整个视口,哪怕页面滚动也不能移位。absolute 会相对于最近的定位祖先,一旦父容器有 overflow: hidden 或 transform,就可能裁剪或错位;fixed 始终以视口为参考,更可靠。
常见错误现象:fixed 遮罩在 iOS Safari 上不随键盘弹出上移,或在某些安卓 WebView 中被输入框顶起——本质是浏览器对 fixed 的实现差异,不是写法错了,而是得配合 viewport 设置或监听 resize 事件微调。
- 必须设
top: 0; left: 0; width: 100vw; height: 100vh,不能只靠100%,否则在缩放或横屏时可能漏边 - 加
z-index要足够高(比如9999),但别盲目堆叠,先查当前页面最高z-index值再定 - 避免给遮罩父元素加
transform、perspective或filter,否则会创建新层叠上下文,让fixed失效
居中加载图标要用 flex + margin: auto 组合
单靠 text-align: center 或 line-height 居中图标,在现代多尺寸场景下极易偏移;flex 是目前最稳的方案,且兼容性已覆盖所有支持 fixed 的环境。
使用场景:遮罩内通常只有图标 + 文字,结构简单,不需要响应式换行或复杂布局,flex 足够轻量。
立即学习“前端免费学习笔记(深入)”;
- 遮罩容器设
display: flex; align-items: center; justify-content: center - 内部加载元素(如
<div class="spinner"></div>)不要设固定宽高,用margin: auto辅助兜底(防老版 Safari flex bug) - 避免用
transform: translate(-50%, -50%)+top: 50%; left: 50%,在 zoom 缩放或高 DPR 屏幕下易出现 1px 偏移
pointer-events: none 和 pointer-events: auto 切换要谨慎
遮罩本身不该拦截点击,但加载中的按钮或表单控件又需要禁用交互。直接给遮罩设 pointer-events: none 很危险——它会让底层所有元素重新响应点击,包括正在异步提交的按钮,可能重复触发。
正确做法是:遮罩保持 pointer-events: auto,但通过控制遮罩的 opacity 和 visibility 来管理显示,同时单独给业务区域加 pointer-events: none。
- 遮罩层始终保留
pointer-events: auto,确保它能“挡住”点击 - 真正要禁用的区域(比如表单容器)动态加类
.is-loading,里面设pointer-events: none - 不要依赖
opacity: 0隐藏遮罩后自动失去拦截能力——opacity不影响事件捕获,必须显式控制pointer-events
移动端键盘弹出时遮罩被顶起或消失
这不是 CSS 写错了,是 Safari 和部分安卓浏览器在软键盘唤起时会重置 fixed 元素的定位基准(从视口变成可视区域),导致遮罩上移或高度塌陷。
性能与兼容性影响:监听 resize 或 focusin 动态重设 height 成本低,但频繁触发可能卡顿;用 vh 单位在 iOS 15+ 已修复,但旧版本仍需降级处理。
- 优先用
height: 100vh,iOS 15+ 和 Chrome 稳定 - 对 iOS window.visualViewport?.height 变化,动态更新遮罩
height - 避免用
document.documentElement.clientHeight,它不随键盘变化实时更新
最麻烦的其实是遮罩和加载逻辑耦合太紧:比如请求结束立刻 remove 遮罩,但动画还没收完,用户就点到了底层按钮。遮罩的显隐最好交由 CSS 过渡控制,JS 只负责开关类名。


















