body { overflow: hidden } 在移动端无效,因 ios webkit 和部分安卓 webview 不拦截 touch 滚动事件;需结合 html fixed 定位、scrolly 记录还原、overscroll-behavior 作用于可滚动子元素,并用 js 精准拦截非可滚动区 touchmove。

仅靠 body { overflow: hidden } 在真机上基本无效,iOS WebKit 和部分安卓 WebView 根本不拦截 body 上的 touch 滚动事件——这不是写法错,是浏览器机制决定的。
为什么 body { overflow: hidden } 在移动端完全失效
iOS Safari 会忽略 body 的 overflow 设置,继续响应手指滑动并触发惯性滚动;键盘弹出、地址栏收起、焦点切换(比如点击 input)时,浏览器还会主动调用 scrollIntoView 或重置视口,导致背景“偷偷动一下”。安卓旧版 WebView(如 UC、QQ)也有类似表现。更麻烦的是,overflow: hidden 只控制渲染层是否显示滚动条,对事件流毫无约束力。
必须同时锁定 html 和 body 的定位与滚动位置
真正起效的做法是让 html 物理脱离滚动容器角色:
- 弹窗打开前,用
window.pageYOffset记录当前滚动位置(别用document.body.scrollTop,iOS 下它常为 0) - 给
html加内联样式:position: fixed; width: 100%; top: -${scrollY}px - 再设
body { overflow: hidden },防键盘顶起内容或布局塌陷 - 关闭时,先清空
html的position和top,再用window.scrollTo(0, scrollY)还原 - 务必用
requestAnimationFrame包裹scrollTo,否则 iOS 下可能闪一下
overscroll-behavior 必须加在可滚动子元素上,不是弹窗根节点
这个属性只对「实际可滚动」的容器生效,直接写在 .modal 或遮罩层上没用:
- 找到弹窗内部真正要滚动的区域,比如
.modal__content - 确保它有明确高度 +
overflow-y: auto(不能只靠max-height) - 加上
.modal__content { overscroll-behavior: contain; } - 如果连弹性回弹都要禁(比如防止干扰表单),才用
none,但兼容性更差 - 注意:iOS 16.4+、Chrome 63+、Firefox 59+ 支持;旧版 Safari 需 JS fallback
JS 补漏必须精准判断可滚动区域,不能全局拦截
在 iOS 12–15 或某些安卓 WebView 中,仍需 JS 拦截,但绝不能对整个 document 绑 touchmove 并粗暴 e.preventDefault():
- 监听弹窗根节点(如
.modal)的touchmove,注册时带{ passive: false } - 用
e.target.closest('.scrollable')判断触摸点是否落在可滚动区(提前给该区域加 class) - 不在可滚动区内才调
e.preventDefault();在就放行 - 弹窗关闭时必须调用
removeEventListener,否则监听残留造成内存泄漏
最易被忽略的是:iOS 键盘唤起时的“闪动”不是 CSS 能修的,它由浏览器主动滚动触发;唯一可靠解法是输入框获得焦点后立即调用 element.scrollIntoView({ block: 'nearest' }),并搭配 setTimeout(() => window.scrollTo(...)) 做兜底。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











