body { overflow: hidden } 在真机上基本无效,因为 ios webkit 和部分安卓 webview 不拦截 body 上的 touch 滚动行为,仅控制渲染不干预事件流;真正生效需用 position: fixed 钉住 body 并配合 top 偏移还原位置,或在实际可滚动子元素上设置 overscroll-behavior(如 .modal__content { overflow-y: auto; overscroll-behavior: contain; }),旧版 safari 需 js 有条件 preventdefault 补漏并精确保存/还原 window.pageyoffset。

为什么 body { overflow: hidden } 在真机上基本无效
这不是你 CSS 写错了,是 iOS WebKit 和部分安卓 WebView(比如旧版 UC、QQ)根本不会拦截 body 上的 touch 滚动行为。视觉上没滚动条,但手指一滑,底层页面照样惯性滚动——overflow: hidden 只管渲染,不管事件流。
常见错误就是只加这行:document.body.style.overflow = 'hidden',然后发现 iPhone 上拖着弹窗还是能带得动背景页。
真正起作用的是让 body 失去滚动能力:用 position: fixed 把它钉死在视口里,再靠 top 偏移还原视觉位置。
overscroll-behavior 是首选,但必须设在可滚动子元素上
这个属性才是现代解法的核心,它不重排、不丢 scroll position、也不触发跳顶,但很多人用错位置——直接写在 .modal 容器上是无效的,因为那个容器本身不可滚动。
正确做法是找到弹窗内部实际滚动的内容区,比如:.modal__content 或 .popup-scroll,确保它有明确高度和 overflow-y: auto,再加:
/* 推荐:允许内部回弹,但切断穿透 */<br>.modal__content {<br> overflow-y: auto;<br> overscroll-behavior: contain;<br>}
如果连回弹都要禁(比如防止下拉刷新干扰),才用 overscroll-behavior: none;iOS 16.4+、Chrome 63+、Firefox 59+ 都支持,旧 Safari 需 fallback。
JS 补漏时别无差别 preventDefault
兼容 iOS 12–14 或某些安卓 WebView,仍需 JS 拦截,但不能对整个遮罩层绑 touchmove 并 e.preventDefault(),否则弹窗里 textarea、长列表全失灵。
推荐做法是监听弹窗根节点,在触摸点不在可滚动子区域时才阻止:
- 给可滚动区加 class,比如
.scrollable - 在 modal 根元素上监听
touchmove - 用
e.target.closest('.scrollable')判断是否在可滚动区内 - 不是就调
e.preventDefault(),是就放行
注意:不要对 document 或 body 绑,会干掉所有原生滚动控件。
滚动位置保存和还原容易被忽略的细节
用 position: fixed 锁定 body 时,scrollTop 不可靠——iOS Safari 中 document.body.scrollTop 常为 0,必须优先读 window.pageYOffset。
关闭弹窗后,还原顺序不能错:
- 先清空
body.style.position和body.style.top - 再调
window.scrollTo(0, savedScrollY) - 不能只改
scrollTop,否则 Safari 下可能失效
复杂点在于:多个弹窗嵌套、快速开关、或用户中途手动滚动,这些场景都需要状态隔离,否则 savedScrollY 会被覆盖或错乱。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











