移动端汉堡菜单展开后页面仍可滚动,根本原因是ios safari和部分android浏览器中滚动容器是html而非body,仅设body{overflow:hidden}无效;必须同时设置html、body{overflow:hidden;height:100%},并用js拦截touchmove事件(passive:false)+记录恢复scrolly位置。

移动端汉堡菜单展开后页面仍可滚动,不是 CSS 写得不够狠,而是只动 body 的 overflow: hidden 在 iOS Safari 和部分 Android 浏览器里根本无效——滚动容器其实是 html,且 touchmove 默认行为会绕过 CSS 限制。
为什么 body { overflow: hidden } 在移动端经常失效
Chrome/Firefox 把根滚动交给 html 元素,body 只是内容流容器;设 body 的 overflow 对实际滚动没影响。iOS Safari 更麻烦:键盘弹出、地址栏收起、甚至手指多滑两下,都会触发底层页面回弹滚动,纯 CSS 锁不住。
-
html和body必须同时设置overflow: hidden+height: 100%,否则html内容撑高后仍可滚 - 仅靠 CSS 不处理
touchmove事件时,手指拖拽仍会触发滚动(尤其在 iOS 上) - 若页面有
transform或will-change层叠上下文,可能意外创建新滚动上下文,导致样式不生效
必须同时操作 HTML 和 body 的 CSS
单独改 body 是常见翻车点。真正起效的最小 CSS 组合是:
html, body {
overflow: hidden;
height: 100%;
}
移动端建议加一层兜底:
@media (max-width: 768px) {
body {
position: fixed;
top: 0;
left: 0;
width: 100%;
}
}
-
position: fixed能绕过 iOS 弹性滚动,但会让body脱离文档流——确保你的菜单遮罩层z-index足够高,且不依赖body的相对定位 - 别漏掉
height: 100%,否则html高度可能由内容撑开,溢出部分仍可滚动
JS 必须拦截 touchmove 且带 { passive: false }
即使 CSS 生效,iOS Safari 的 touchmove 默认行为仍会滚动页面。必须显式阻止:
let isMenuOpen = false;
function toggleMenu() {
const body = document.body;
const html = document.documentElement;
if (isMenuOpen) {
body.classList.remove('disabledScroll');
html.classList.remove('disabledScroll');
// 恢复前先移除监听
document.removeEventListener('touchmove', preventDefault, { passive: false });
} else {
body.classList.add('disabledScroll');
html.classList.add('disabledScroll');
document.addEventListener('touchmove', preventDefault, { passive: false });
}
isMenuOpen = !isMenuOpen;
}
function preventDefault(e) {
e.preventDefault();
}
- 监听必须加
{ passive: false },否则 Safari/Chrome 会忽略preventDefault() - 不要绑定到
document全局——如果菜单里有轮播图或可拖拽元素,它们也会被锁死;但这里目标是禁背景滚动,全局监听是合理选择 - 记得在菜单关闭时移除监听,否则内存泄漏,且后续页面滚动可能异常
别忘了保存并恢复滚动位置
菜单关闭后直接恢复 overflow,页面可能卡在中间位置或顶部跳动。必须记录并还原 window.scrollY:
let scrollY = 0;
function openMenu() {
scrollY = window.scrollY;
document.documentElement.style.position = 'fixed';
document.documentElement.style.top = `-${scrollY}px`;
document.body.style.overflow = 'hidden';
}
function closeMenu() {
document.documentElement.style.position = '';
document.documentElement.style.top = '';
document.body.style.overflow = '';
window.scrollTo(0, scrollY);
}
-
document.documentElement.style.position = 'fixed'是比单纯改overflow更可靠的“钉住”方案,尤其防 iOS 键盘唤起时自动滚动 - 恢复时用
window.scrollTo(0, scrollY),而不是scrollTop,兼容性更好 - 这个逻辑不能省——否则用户关掉菜单后发现页面“不见了”,得手动往上拉,体验极差
最易被忽略的是:iOS 键盘弹出时,哪怕你已锁住页面,浏览器仍会强行把聚焦元素滚进视口。这和你的菜单逻辑无关,但用户感知一样——得在弹窗内 input 获取焦点时,调用 element.scrollIntoView({ block: 'nearest' }) 并配合 setTimeout(() => window.scrollTo(0, scrollY), 100) 强制回位。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











