不滚动?局部滚动才高级:前端滚动区域实战指南
- 页面不滚动?局部滚动才高级:前端滚动区域实战指南
- 当整个页面“冻住”,只有部分内容在悄悄滑动
- 滚动条的前世今生:从原生 overflow 到现代 CSS 新特性
- 深入理解局部滚动的核心机制:容器、内容与 overflow 的三角关系
- 1. 容器必须“限高”
- 2. 内容必须“溢出”
- 3. overflow 的“四小花旦”
- CSS 实现局部滚动的多种姿势:overflow、scrollbar 样式、容器高度控制全解析
- 姿势 1:固定高度 + overflow:auto(后台列表最爱)
- 姿势 2:flex 剩余空间滚动(聊天窗口标配)
- 姿势 3:横向滚动瀑布流(卡片不折行)
- 姿势 4:隐藏滚动条但保留功能(产品经理最爱)
- JavaScript 动态控制滚动区域:监听、定位与平滑滚动技巧
- 1. 平滑滚动到指定元素(无需插件)
- 2. 无限滚动(IntersectionObserver 版)
- 3. 聊天自动底部吸附(新消息来了贴底)
- 4. 自定义滚动条拖拽(简单粗暴版)
- 移动端的特殊挑战:iOS 弹性滚动、安卓兼容性与触摸事件处理
- 1. iOS 的“橡皮筋”玄学
- 2. 安卓的“闪烁”与“回流”
- 3. 阻止“整页回弹”的终极黑魔法
- 性能陷阱与优化策略:滚动卡顿、重绘回流、虚拟滚动初探
- 1. 卡成 PPT 的元凶
- 2. 优化三板斧
- 3. 虚拟滚动(Virtual Scroll)入门
- 调试滚动异常的侦探手册:为什么我的局部滚动不生效?
- 排查清单(打印出来贴显示器)
- 实用开发技巧大放送:隐藏滚动条但保留功能、自定义滚动条样式、滚动联动交互
- 技巧 1:滚动条“隐身”但鼠标滚轮健在
- 技巧 2:双栏联动(左侧目录高亮,右侧内容滚)
- 技巧 3:滚动条“渐隐”动画(逼格 +1)
- 让滚动更聪明:结合 Intersection Observer 实现懒加载与动画触发
- 1. 图片懒加载(前文已给),再送“背景图”懒加载:
- 2. 滚动触发“淡入”动画(AOS 的极简替代)
- 3. 数字滚动累加(简历里写“提升用户体验 90%”就靠它)
- 尾声:把“滚动”做成一种体验,而不是一种功能
页面不滚动?局部滚动才高级:前端滚动区域实战指南
当整个页面“冻住”,只有部分内容在悄悄滑动
你有没有过这样的体验:打开一个后台系统,左侧菜单稳如磐石,右侧表格却像脱缰的野马,上下翻飞;或者刷着手机,顶部导航纹丝不动,商品卡片在指尖丝滑穿梭。那一刻,你突然意识到——“原来页面可以不整体滚动!”
别笑,我第一次见到这种玩法时,差点把键盘上的咖啡喷出来。整页滚动就像大排档的炒面,管饱但粗糙;局部滚动则是深夜食堂的厚蛋烧,精致、克制,还带点仪式感。今天,我们就把这块“厚蛋烧”的做法彻底拆给你看:从 CSS 的“祖传 overflow”,到 JavaScript 的“滚动劫持”,再到移动端的“弹性玄学”,甚至性能优化的“虚拟滚动”黑魔法,一个都不放过。读完这篇,你也能让页面该动的地方动,该静的地方静,老板看了都说“高级”。
滚动条的前世今生:从原生 overflow 到现代 CSS 新特性
故事要从 1998 年说起。那一年,CSS 2 规范里悄悄塞进了两个单词:overflow: auto。没人想到,这对小兄弟日后会成为网页布局的“流量担当”。
在 IE6 称王称霸的年代,我们想给div塞滚动条,只能老老实实写:
.scroll-box{width:300px;height:200px;overflow:auto;/* 超出就滚,不超出就静 */}简单、直接,但丑得令人发指——灰灰的滚动条像没擦干净的油烟机,宽度还不可调。
后来,WebKit 内核异军突起,苹果率先在 2009 年带来-webkit-scrollbar系列私有属性,前端er 第一次尝到“自定义滚动条”的甜头:
/* 给滚动条整容 */.chat-list::-webkit-scrollbar{width:6px;}.chat-list::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#ff8a00,#e52e71);border-radius:3px;}.chat-list::-webkit-scrollbar-track{background:rgba(0,0,0,0.05);}再后来,CSS 工作组的大佬们觉得老让开发者写私有前缀太掉价,于是 CSS Scrollbars Module 横空出世,scrollbar-width和scrollbar-color走进标准:
/* Firefox 已支持 */.firefox-box{scrollbar-width:thin;/* auto | thin | none */scrollbar-color:#ff8a00 #f0f0f0;/* 滑块颜色 轨道颜色 */}一句话总结:滚动条从“能用”到“好看”,花了整整 20 年。而我们,站在巨人的肩膀上,只需要写几行代码,就能把“油烟机”换成“北欧极简风”。
深入理解局部滚动的核心机制:容器、内容与 overflow 的三角关系
想玩局部滚动,先得搞懂“三角恋”:
- 容器(Container):舞台,决定“谁”可以滚。
- 内容(Content):演员,决定“有没有”必要滚。
- overflow:导演,喊 Action 的那一位。
1. 容器必须“限高”
没限高=没边界,内容再长也滚不动。
错误示范:
<divclass="parent"><divclass="child">…超长内容…</div></div><style>.parent{overflow:auto;}/* 没给高度,滚个寂寞 */</style>正确姿势:
.parent{height:100%;/* 或者固定 300px */overflow:auto;/* 或者 scroll / hidden + JS 自定义 */}2. 内容必须“溢出”
如果子元素身高只有 150px,而容器 300px,那滚动条会傲娇地拒绝出场。
调试小技巧:给容器加outline: 1px solid red,一眼看清边界。
3. overflow 的“四小花旦”
| 属性值 | 行为描述 |
|---|---|
| visible | 默认,溢出不剪,也不滚 |
| hidden | 溢出剪掉,不滚 |
| scroll | 永远显示滚动条,溢出与否都占空间 |
| auto | 智能,溢出才出现滚动条,不溢出则隐身 |
记住口诀:“auto 最常用,scroll 做备胎,hidden 做剪裁,visible 看天意。”
CSS 实现局部滚动的多种姿势:overflow、scrollbar 样式、容器高度控制全解析
姿势 1:固定高度 + overflow:auto(后台列表最爱)
<divclass="table-wrapper"><table>…几十行 tr…</table></div>.table-wrapper{max-height:60vh;/* 视口高度的 60%,自适应 */overflow:auto;border:1px solid #e8e8e8;}姿势 2:flex 剩余空间滚动(聊天窗口标配)
<sectionclass="chat"><header>Header</header><mainclass="chat-list">…消息…</main><footer>Input</footer></section>.chat{height:100vh;display:flex;flex-direction:column;}.chat-list{flex:1;/* 占据剩余高度 */overflow:auto;}姿势 3:横向滚动瀑布流(卡片不折行)
.horizontal-scroll{white-space:nowrap;/* 强制不换行 */overflow-x:auto;scroll-snap-type:x mandatory;/* 子元素对齐 */}.horizontal-scroll .card{display:inline-block;width:260px;scroll-snap-align:start;}姿势 4:隐藏滚动条但保留功能(产品经理最爱)
.hide-scrollbar{overflow:auto;/* 对 WebKit 生效 */&::-webkit-scrollbar{display:none;}/* 对 IE/Edge 生效 */-ms-overflow-style:none;/* 对 Firefox 生效 */scrollbar-width:none;}一行注释写清楚:“隐藏不是砍掉,滚轮、触摸、JS 照样能用,只是颜值党不想看到那条黑杠杠。”
JavaScript 动态控制滚动区域:监听、定位与平滑滚动技巧
CSS 负责“静态造型”,JS 才是“动态灵魂”。下面直接丢给你一套“滚动组合拳”,复制就能打。
1. 平滑滚动到指定元素(无需插件)
// 传统写法,一行解千愁document.querySelector('.target').scrollIntoView({behavior:'smooth'});// 进阶:控制父容器滚动,避免整页跳functionscrollToRow(list,index){constitem=list.children[index];if(!item)return;consttop=item.offsetTop;list.scrollTo({top,behavior:'smooth'});}2. 无限滚动(IntersectionObserver 版)
constlist=document.querySelector('.feed');constfooter=document.querySelector('.footer-sentinel');constio=newIntersectionObserver(entries=>{if(entries[0].isIntersecting){loadMore().then(newHtml=>{list.insertAdjacentHTML('beforeend',newHtml);});}},{root:list,rootMargin:'100px'});io.observe(footer);3. 聊天自动底部吸附(新消息来了贴底)
functionkeepBottom(box){// 判断是否已贴底:距离底部 < 10pxconstisBottom=box.scrollHeight-box.scrollTop-box.clientHeight<10;if(isBottom){// 等 DOM 更新完再滚requestAnimationFrame(()=>{box.scrollTop=box.scrollHeight;});}}// 监听新消息mutObs=newMutationObserver(()=>keepBottom(box));mutObs.observe(box,{childList:true});4. 自定义滚动条拖拽(简单粗暴版)
constthumb=scrollbar.querySelector('.thumb');lety=0,startTop=0;thumb.addEventListener('mousedown',e=>{y=e.pageY;startTop=thumb.offsetTop;document.addEventListener('mousemove',move);document.addEventListener('mouseup',up);});functionmove(e){constdelta=e.pageY-y;thumb.style.top=`${startTop+delta}px`;// 同步内容滚动constmax=container.scrollHeight-container.clientHeight;container.scrollTop=(thumb.offsetTop/(scrollbar.clientHeight-thumb.clientHeight))*max;}functionup(){document.removeEventListener('mousemove',move);document.removeEventListener('mouseup',up);}移动端的特殊挑战:iOS 弹性滚动、安卓兼容性与触摸事件处理
1. iOS 的“橡皮筋”玄学
iOS Safari 给body加了祖传“橡皮筋”,但你要是敢把overflow:hidden往body上一怼,整个页面瞬间变石雕。解决方案:
把滚动交给“内部容器”,并开启“弹性开关”:
.scroll-ios{overflow-y:scroll;-webkit-overflow-scrolling:touch;/* 灵魂属性 */}注意:
- 容器必须独立“堆叠上下文”,否则橡皮筋依旧。
- 如果用了
position:fixed遮罩,记得加@supports (-webkit-touch-callout: none)做降级,不然软键盘弹出时分分钟教你做人。
2. 安卓的“闪烁”与“回流”
部分国产安卓的 WebView 在滚动时会“闪白”,原因是背景没固定:
.scroll-android{overflow:auto;background-attachment:local;/* 关键 */}3. 阻止“整页回弹”的终极黑魔法
// 限定滚动区域,body 禁止滚document.body.addEventListener('touchmove',e=>{if(!e.target.closest('.scroll-local')){e.preventDefault();// 让 body 稳如狗}},{passive:false});性能陷阱与优化策略:滚动卡顿、重绘回流、虚拟滚动初探
1. 卡成 PPT 的元凶
- 重绘:滚动条滑一次,全页面红一遍(DevTools Rendering 面板里 Paint flashing 狂闪)。
- 回流:改了元素宽高,浏览器重新计算位置。
- 图片解码:几千张头像同时加载,主线程直接罢工。
2. 优化三板斧
- 合成层升级:
will-change: transform把元素抬进 GPU 独立图层,减少重绘。 - 防抖 + 节流:滚动监听 16ms 一次就够了,别贪心。
- 懒加载:下面这段“最简懒加载”拿去用,不谢。
constio=newIntersectionObserver(entries=>{entries.forEach(en=>{if(en.isIntersecting){constimg=en.target;img.src=img.dataset.src;io.unobserve(img);}});});document.querySelectorAll('img[data-src]').forEach(img=>io.observe(img));3. 虚拟滚动(Virtual Scroll)入门
如果列表超过 1000 行,DOM 再薄也扛不住。思路:只渲染可视区域 + 上下缓冲带。
伪代码 50 行,跑通原理:
constitemHeight=48;constbuffer=5;consttotal=10000;list.addEventListener('scroll',()=>{constscrollTop=list.scrollTop;conststart=Math.floor(scrollTop/itemHeight);constend=start+Math.ceil(list.clientHeight/itemHeight);constslice=data.slice(Math.max(0,start-buffer),end+buffer);render(slice);// 只渲染 slice 这一段// 用 paddingTop/paddingBottom 撑开滚动高度list.firstElementChild.style.paddingTop=`${start*itemHeight}px`;list.lastElementChild.style.paddingBottom=`${(total-end)*itemHeight}px`;});真要用?别重复造轮子,react-window、vue-virtual-scroller、ag-grid 都帮你封装好了,但原理必须懂,面试会考。
调试滚动异常的侦探手册:为什么我的局部滚动不生效?
排查清单(打印出来贴显示器)
- 容器有没有“限高”?
→getComputedStyle(box).height是不是auto? - 内容有没有“溢出”?
→ 加outline看一眼边界。 - 父级有没有
transform?
→transform会创建 containing block,position:fixed子元素可能“叛逃”。 - iOS 上滚不动?
→-webkit-overflow-scrolling: touch写了没? - 安卓闪白?
→ 给滚动容器加background-color,别透明。 - 写了
scroll-behavior: smooth却瞬移?
→ 被scrollTo({behavior:'instant'})覆盖了,检查冲突脚本。 - 自定义滚动条失踪?
→ 只在 WebKit 生效,Firefox 用scrollbar-color,IE 直接放弃。
实用开发技巧大放送:隐藏滚动条但保留功能、自定义滚动条样式、滚动联动交互
技巧 1:滚动条“隐身”但鼠标滚轮健在
上面已给代码,再补一句:产品经理如果非要“苹果风格”,把宽度设为 0 也能过审:
::-webkit-scrollbar{width:0!important;}技巧 2:双栏联动(左侧目录高亮,右侧内容滚)
constheadings=content.querySelectorAll('h2');constlinks=toc.querySelectorAll('a');content.addEventListener('scroll',throttle(()=>{letcurrent='';headings.forEach(h=>{if(h.offsetTop-80<=content.scrollTop)current=h.id;});links.forEach(a=>a.classList.toggle('active',a.hash===`#${current}`));},100));技巧 3:滚动条“渐隐”动画(逼格 +1)
.scrollbar-fade{overflow:auto;transition:scrollbar-color 0.3s;}.scrollbar-fade:not(:hover){scrollbar-color:transparent transparent;/* Firefox */}.scrollbar-fade:not(:hover)::-webkit-scrollbar-thumb{background:transparent;/* WebKit */}让滚动更聪明:结合 Intersection Observer 实现懒加载与动画触发
1. 图片懒加载(前文已给),再送“背景图”懒加载:
<divclass="bg-lazy"data-bg="hero.jpg">…</div>constioBg=newIntersectionObserver(entries=>{entries.forEach(en=>{if(en.isIntersecting){constdiv=en.target;div.style.backgroundImage=`url(${div.dataset.bg})`;ioBg.unobserve(div);}});});document.querySelectorAll('.bg-lazy').forEach(el=>ioBg.observe(el));2. 滚动触发“淡入”动画(AOS 的极简替代)
.fade-up{opacity:0;transform:translateY(30px);transition:0.6s;}.fade-up.visible{opacity:1;transform:translateY(0);}constioFade=newIntersectionObserver(entries=>{entries.forEach(en=>{if(en.isIntersecting)en.target.classList.add('visible');});},{threshold:0.1});document.querySelectorAll('.fade-up').forEach(el=>ioFade.observe(el));3. 数字滚动累加(简历里写“提升用户体验 90%”就靠它)
functioncountUp(el,target){letn=0;consttimer=setInterval(()=>{n+=Math.ceil(target/50);if(n>=target){n=target;clearInterval(timer);}el.textContent=n.toLocaleString();},16);}constioCount=newIntersectionObserver(entries=>{entries.forEach(en=>{if(en.isIntersecting){consttarget=+en.target.dataset.to;countUp(en.target,target);ioCount.unobserve(en.target);}});});document.querySelectorAll('.count-up').forEach(el=>ioCount.observe(el));尾声:把“滚动”做成一种体验,而不是一种功能
局部滚动就像一把雕刻刀,用好了,页面层次分明、交互丝滑;用不好,卡顿、兼容、性能三座大山一起砸下来。本文从 CSS 的“祖传 overflow”讲到虚拟滚动的“黑科技”,再奉上一堆“复制可用”的代码片段,目的就是让你在面对“页面不滚动”的需求时,不再心里咯噔一下,而是嘴角上扬:“这个我熟。”
下次产品经理说“能不能让这里单独滚”,你就可以把这篇文章甩给他——不对,还是别甩,自己偷偷收藏就好。毕竟,高手向来低调,只有滚动条在悄悄发光。
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!
| 专栏系列(点击解锁) | 学习路线(点击解锁) | 知识定位 |
|---|---|---|
| 《微信小程序相关博客》 | 持续更新中~ | 结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等 |
| 《AIGC相关博客》 | 持续更新中~ | AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结 |
| 《HTML网站开发相关》 | 《前端基础入门三大核心之html相关博客》 | 前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识 |
| 《前端基础入门三大核心之JS相关博客》 | 前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。 通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心 | |
| 《前端基础入门三大核心之CSS相关博客》 | 介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页 | |
| 《canvas绘图相关博客》 | Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化 | |
| 《Vue实战相关博客》 | 持续更新中~ | 详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅 |
| 《python相关博客》 | 持续更新中~ | Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具 |
| 《sql数据库相关博客》 | 持续更新中~ | SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能 |
| 《算法系列相关博客》 | 持续更新中~ | 算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维 |
| 《IT信息技术相关博客》 | 持续更新中~ | 作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识 |
| 《信息化人员基础技能知识相关博客》 | 无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方 | |
| 《信息化技能面试宝典相关博客》 | 涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面 | |
| 《前端开发习惯与小技巧相关博客》 | 持续更新中~ | 罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等 |
| 《photoshop相关博客》 | 持续更新中~ | 基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结 |
| 日常开发&办公&生产【实用工具】分享相关博客》 | 持续更新中~ | 分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具 |
吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!