最可靠方案是给body加overflow:hidden并用position:fixed使遮罩层覆盖全屏,同时用padding-right补偿滚动条宽度、touch-action:none解决ios穿透,并通过css变量--scroll-y配合js锁定滚动位置。

弹窗出现时页面不能滚动,但遮罩层要铺满整个视口
关键不是“禁止滚动”,而是让 body 失去滚动能力,同时确保遮罩层(overlay)不随页面内容被裁剪或错位。最可靠的方式是给 body 加 overflow: hidden,并把遮罩层用 position: fixed 覆盖全屏。
- 不要只给遮罩层加
overflow: hidden—— 它不影响body滚动,用户仍可滚背景 - 遮罩层必须设
top: 0; left: 0; width: 100vw; height: 100vh;,不能依赖100%(百分比在 relative 父容器下会失效) - 如果页面已有横向滚动条,
100vw会包含滚动条宽度,导致遮罩右侧留白;此时可用width: 100%+position: fixed配合html { overflow-x: hidden }控制
弹窗关闭后恢复滚动,但避免抖动或跳动
页面突然禁用滚动时,body 会因滚动条消失而变宽(尤其 Chrome),导致内容右移。关闭弹窗时若直接移除 overflow: hidden,又会瞬间回弹,肉眼可见抖动。
- 在弹窗打开前,先记录
document.documentElement.scrollLeft和scrollTop,关闭时手动 restore(仅对需要锚点定位的场景) - 更通用的解法:给
body加padding-right: calc(100vw - 100%);,自动补偿滚动条宽度,避免布局偏移 - 不要用
body.style.overflow = ''清空样式 —— 应该还原为原始值(比如从getComputedStyle(document.body).overflow缓存初始值)
移动端 Safari 上遮罩层点击穿透或滚动失效
iOS Safari 对 fixed 元素和 overflow: hidden 的处理有差异:禁用 body 滚动后,遮罩层可能无法响应 touchstart,或手指划过遮罩时背景仍会滚动。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 必须给遮罩层加
touch-action: none,否则 iOS 默认允许区域内的惯性滚动穿透到下层 - 遮罩层需有明确的
backface-visibility: hidden或will-change: transform,触发硬件加速,避免渲染异常 - 某些 iOS 版本下,仅靠
overflow: hidden不够,需额外加body { position: fixed; top: -${scrollTop}px; }锁定位置(关闭时再还原)
用纯 CSS 实现,不依赖 JavaScript 控制 body 样式
完全可行,但需配合 class 切换。核心是定义一个开关类(如 modal-open),由 JS 控制是否加到 html 或 body 上,CSS 做响应式约束。
html.modal-open {
overflow: hidden;
}
html.modal-open body {
position: relative;
top: calc(-1 * var(--scroll-y, 0px));
}
.overlay {
position: fixed;
top: 0; left: 0;
width: 100vw; height: 100vh;
touch-action: none;
backface-visibility: hidden;
}
注意:CSS 变量 --scroll-y 需由 JS 在打开前写入,否则无法精准锁定位置。纯 CSS 方案适合简单场景,复杂交互仍建议 JS 主导控制逻辑。
真正麻烦的不是加遮罩,而是滚动锁定在不同浏览器、不同设备上的行为差异。iOS 和 Android 的 touch 行为、滚动条宽度计算、fixed 定位边界,每个都可能单独出问题。别指望一套样式走天下,得留好钩子做 UA 分支处理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










