侧边菜单拉出时页面仍可滚动,因body未被锁住;可靠方案是设body为position:fixed、记录并还原scrollTop,加touch-action:pan-y于菜单根元素,遮罩层需pointer-events:auto+preventDefault并正确设置z-index和height。

侧边菜单拉出时,为什么页面还会滚动?
根本不是 JS 绑定漏了,而是 body 本身没被“锁住”。移动端浏览器默认允许整个页面滚动,哪怕你用 position: fixed 盖住了菜单,手指在菜单上滑动时,只要触发了 touchmove 且没阻止,默认行为就是滚背景——尤其 iOS Safari 会把菜单区域也当作可滚动容器的一部分。
用 body 定位偏移 + scrollTop 记录最稳
直接设 html 或 body 的 overflow: hidden 会导致页面跳回顶部,这是硬伤。真正实测可靠的方案是:临时把 body 设为 position: fixed,并用 scrollTop 记录原始位置,关闭时再还原。
-
document.documentElement.scrollTop和document.body.scrollTop都要读,兼容不同渲染模式 - 偏移值必须带负号:
top: -${scrollTop}px,否则页面会向下闪一下 - 加
width: 100vw,防止因position: fixed导致宽度收缩、出现横向滚动条 - 别忘了
left: 0,否则某些安卓浏览器会右偏
菜单容器自身必须加 touch-action: pan-y
如果侧边菜单里有长列表或可滚动区域(比如用户资料项、分类树),手指上下滑它时,不加这个声明就容易触发整页横向拖拽或缩放——尤其在 iOS 上,touch-action: none 会彻底禁掉所有手势,而 pan-y 才是精准放开纵向滚动、禁止横向拖动的钥匙。
- 只加在菜单根元素上,比如
.sidebar,不要加到子项或ul上 - 配合
overflow-y: auto使用才生效;如果用了overflow: hidden,这个声明就白加 - Chrome 和 Safari 都支持,无需前缀
遮罩层(.overlay)层级和事件穿透不能错
遮罩层不只是视觉覆盖,更是滚动拦截的第一道防线。它必须和菜单同级、z-index 高于主内容但低于菜单,且要主动捕获 touchmove 并 preventDefault()。
立即学习“前端免费学习笔记(深入)”;
-
.overlay的z-index建议设为999,菜单设为1000,主内容保持默认或1 - 遮罩层必须显式设置
height: 100vh,不能靠min-height,否则在部分安卓浏览器中会漏出底部空白 - 别依赖
pointer-events: none——它会让遮罩层无法拦截 touch 事件,必须用pointer-events: auto+preventDefault
最易忽略的是:菜单关闭后,body 的 position: static 恢复必须同步触发 window.scrollTo(),否则用户会发现页面卡在半空——这不是 bug,是滚动锚点没归位。



















