移动端侧边栏滚动时禁止页面body滚动的实现方案
解决移动端Fixed侧边栏滚动时禁止Body滚动的方案
嘿,这个移动端fixed侧边栏的滚动冲突问题我太熟了——简直是前端开发里的经典踩坑点!我来给你一套经过实战验证的解决方案,完美解决你说的「侧边栏滚动时body跟着滚」的问题,同时保证侧边栏自身内容溢出时能正常滚动。
核心思路
要实现这个效果,我们需要做到两点:
- 侧边栏滑出时,锁定body的滚动,但不能影响侧边栏自身的滚动
- 处理iOS端的「滚动穿透」问题(就是你视频里展示的那种,侧边栏滚到底/顶后继续滑,body会跟着动的情况)
具体实现步骤
1. 基础CSS配置
先给侧边栏和body锁定状态做好样式铺垫:
/* 侧边栏基础样式 */ .sidebar { position: fixed; top: 0; left: -100%; /* 默认隐藏在左侧 */ width: 80%; /* 可根据需求调整宽度 */ height: 100vh; background-color: #fff; overflow-y: auto; /* 内容溢出时允许自身滚动 */ -webkit-overflow-scrolling: touch; /* 让iOS端滚动更顺滑,避免橡皮筋滚动穿透 */ transition: left 0.3s ease; /* 滑出/滑入的过渡动画 */ } /* 侧边栏滑出时的状态 */ .sidebar.open { left: 0; } /* body锁定滚动的类 */ .body-locked { overflow: hidden; position: fixed; width: 100%; /* 避免fixed导致宽度收缩 */ }
2. JavaScript逻辑处理
接下来用JS控制侧边栏的打开/关闭,同时处理body滚动锁定和滚动穿透问题:
// 获取DOM元素 const sidebar = document.querySelector('.sidebar'); const openSidebarBtn = document.querySelector('.open-sidebar-btn'); const closeSidebarBtn = document.querySelector('.close-sidebar-btn'); // 记录打开侧边栏前的页面滚动位置 let preScrollTop = 0; // 打开侧边栏的逻辑 openSidebarBtn.addEventListener('click', () => { // 保存当前滚动位置,避免锁定body后页面跳回顶部 preScrollTop = window.pageYOffset || document.documentElement.scrollTop; // 给body添加锁定类,并设置top值抵消滚动位置变化 document.body.classList.add('body-locked'); document.body.style.top = `-${preScrollTop}px`; // 滑出侧边栏 sidebar.classList.add('open'); // 添加滚动穿透阻止监听 sidebar.addEventListener('touchmove', preventScrollThrough, { passive: false }); }); // 关闭侧边栏的逻辑 closeSidebarBtn.addEventListener('click', () => { // 移除body锁定类,恢复默认样式 document.body.classList.remove('body-locked'); document.body.style.top = ''; // 滑入侧边栏 sidebar.classList.remove('open'); // 移除滚动穿透监听 sidebar.removeEventListener('touchmove', preventScrollThrough); // 回到打开侧边栏前的滚动位置 window.scrollTo(0, preScrollTop); }); // 阻止滚动穿透的核心函数 function preventScrollThrough(e) { const isScrollTop = sidebar.scrollTop === 0; const isScrollBottom = sidebar.scrollHeight - sidebar.scrollTop === sidebar.clientHeight; // 当侧边栏已经滚到顶部/底部时,阻止默认触摸滚动事件,避免触发body滚动 if ((isScrollTop && e.deltaY < 0) || (isScrollBottom && e.deltaY > 0)) { e.preventDefault(); } }
关键细节说明
-webkit-overflow-scrolling: touch:这个属性是iOS端的关键,它会让侧边栏使用原生滚动效果,不仅更顺滑,还能减少滚动穿透的概率。- 记录滚动位置:直接给body加
overflow: hidden会导致页面跳回顶部,所以我们要先保存滚动位置,关闭侧边栏后再恢复回去。 - 滚动穿透阻止函数:当侧边栏已经滚到边界时,阻止默认的触摸事件,彻底避免body跟着滚动的情况。
兼容性说明
这套方案兼容绝大多数移动端浏览器,包括iOS Safari和Android各大浏览器。如果你的项目不需要兼容太老的浏览器,也可以尝试用overscroll-behavior: contain属性直接给侧边栏设置,但这个属性在一些旧设备上支持不佳,所以上面的方案更稳妥。
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

