遮罩层没盖住整个视口,根本原因是未同时设置position: fixed和top/right/bottom/left: 0;若父元素存在transform/filter等属性会创建新层叠上下文,导致z-index失效;还需配合body{overflow: hidden}防滚动,并将遮罩挂载至body下而非局部组件内。

遮罩层没盖住整个视口,先看是否用了 position: fixed 但没设四边为 0
只写 position: fixed 不等于自动铺满——必须显式声明 top: 0、right: 0、bottom: 0、left: 0。浏览器不会“猜”你要全屏。width: 100vw 和 height: 100vh 看似合理,但在有滚动条的页面中,100vw 会包含滚动条宽度,导致水平溢出;100vh 在 iOS Safari 中随地址栏收起/展开跳变,底部常留白。
正确写法是:
.overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 9999;
}
- 不要用
width: 100%+height: 100%替代四边 0 —— 它依赖父容器尺寸,在fixed下行为不可靠 - 避免
display: inline或display: inline-block,它们会让top/left失效 - 如果遮罩里要居中内容,直接在
.overlay上加display: flex; justify-content: center; align-items: center;
遮罩被局部“困住”,检查父元素是否创建了新层叠上下文
即使 z-index: 9999 写得再高,只要遮罩的某个祖先元素有 transform、filter、opacity: 0.99、perspective 或 will-change,就会创建新的层叠上下文(stacking context),让遮罩的 z-index 只在那个父容器内生效——它再也“出不去”了。
常见隐形陷阱:
立即学习“前端免费学习笔记(深入)”;
-
transform: translateZ(0)、transform: translateY(1px)等任意transform值 -
filter: blur(1px)、opacity: 0.99(注意:不是只有opacity: 0才触发) -
will-change: transform(尤其在动画组件里滥用时)
验证方法:打开开发者工具 → 选中遮罩层 → 查看 “Computed” 面板里的 “Containing block”。如果显示的是某个 div 名称,而不是 (root),说明它被限制在那个父容器的层叠上下文中。
遮罩下还能滚动?body 的 overflow 没锁死
position: fixed 遮罩本身不会阻止底层滚动。用户仍可拖动页面,尤其在 iOS 上——这不是 bug,是默认行为。仅靠 pointer-events: none 无法解决,因为滚动事件不走 pointer 事件链路。
必须在遮罩激活时,给 body 加 overflow: hidden:
document.body.classList.add('no-scroll');
CSS 对应:
.no-scroll {
overflow: hidden;
}
- 关闭遮罩后务必移除该 class,否则页面永远不能滚动
- 不要依赖
user-scalable=no,现代浏览器已忽略它 - 如果遮罩内有可滚动区域(如长列表弹窗),需单独给内部容器设
overflow-y: auto,且确保其z-index高于遮罩背景
框架里遮罩失效?DOM 挂载位置错了
Vue/React 组件中,遮罩常被渲染在某个带 transform 的布局容器(比如 .layout 或 .app)内部。一旦这个父容器触发新层叠上下文,遮罩就彻底失去全局覆盖能力。
最稳妥的做法是绕过组件树,直接挂到 document.body 底部:
const overlay = document.createElement('div');
overlay.className = 'overlay';
document.body.appendChild(overlay);
- 不要用
v-if或{show && <overlay></overlay>}把遮罩塞进局部组件里 - 如果必须用组件封装,确保它通过
createPortal(React)或Teleport(Vue 3)渲染到body下 - Bootstrap 等 UI 库的
.modal-backdrop默认 z-index 是 1040,你的遮罩至少设z-index: 1060,并确认没被同名 class 覆盖
真正麻烦的从来不是怎么写 CSS,而是遮罩 DOM 被谁包裹、被谁限制、被谁悄悄改写了层叠关系——这些细节在开发者工具里一眼就能看到,但容易被跳过。


















