拓冰建站拓冰建站
首页 / 资讯中心 / 正文

现代 JavaScript 教程:scroll 滚动事件实战——无限滚动加载、回到顶部按钮与图片懒加载

文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载本文基于开源仓库 en.javascript.infoModern JavaScript Tutorial中 scroll 滚动事件 一章整理而成。scroll事件是浏览器 UI 交互的核心能力之一它可以让我们感知用户滚动页面的时机从而按需显示/隐藏控件、在滚动到底部时加载更多数据或对图片做懒加载以节省流量。读完本文你将掌握scroll事件在window与可滚动元素上的监听方式、阻止页面滚动的正确姿势以及三个可直接运行的实战方案无限滚动页面、回到顶部按钮和图片按需加载。一、scroll 事件基础监听滚动、获取滚动位置scroll事件允许我们对页面或某个元素的滚动做出响应。它同时作用于window和可滚动的元素上窗口滚动时触发window上的scroll事件某个拥有滚动条的元素滚动时则触发该元素上的scroll事件。利用这个事件可以做很多实用的事情例如根据用户当前在文档中的位置显示/隐藏额外的控件或信息例如回到顶部按钮当用户滚动到页面底部时加载更多数据无限滚动、分页加载。原文档给出了一个最基础的小函数用来实时显示当前滚动位置window.addEventListener(scroll, function() { document.getElementById(showScroll).innerHTML window.pageYOffset px; });这里window.pageYOffset返回当前页面在垂直方向上滚过的像素数等价于window.scrollY。把监听器挂到window上后每次窗口滚动都会更新页面上showScroll元素的内容从而直观地看到滚动量。要点小结关注点说明事件名称scroll监听对象window窗口滚动与任意可滚动元素元素滚动获取滚动距离window.pageYOffset/window.scrollY垂直、window.pageXOffset/window.scrollX水平触发时机滚动发生时且滚动过程中会高频触发见下文性能提示由于滚动过程中事件会被高频触发若在处理器里做大量 DOM 操作或重计算容易造成性能问题。常见的优化手段是使用节流throttle包装处理器本教程的 call/apply 与装饰器 一章中专门讲解了 throttle/debounce 装饰器的实现可以作为进阶参考。二、阻止滚动为什么 onscroll 里 preventDefault() 无效很多人会自然地想在scroll事件处理器里调用event.preventDefault()来禁止滚动。原文档明确指出——这样做是无效的因为scroll事件在滚动已经发生之后才触发此时已经木已成舟无法通过它来取消这次滚动。正确的思路是对引发滚动的那个事件调用preventDefault()。例如键盘的PageUppageUp和PageDownpageDown键会引发滚动我们可以拦截这两个按键事件window.addEventListener(keydown, function(e) { if (e.key PageUp || e.key PageDown) { e.preventDefault(); // 阻止该按键引发的滚动 } });这样当用户按下PageUp/PageDown时页面就不会滚动了。但原文档同时提醒引发滚动的方式有很多种滚轮、拖动滚动条、触屏手势、程序化调用scrollTo等逐个拦截既繁琐又不可靠。因此最稳妥的做法是使用 CSS 的overflow属性从根源上禁滚。例如/* 禁止窗口/元素滚动 */ html, body { overflow: hidden; } /* 或者针对某个容器 */ .scrollable { overflow: hidden; /* 完全隐藏滚动 */ /* 或者 overflow: auto / scroll 允许滚动 */ }把overflow设为hidden后元素内容超出部分不可见且不可滚动这是让某物不可滚动最可靠的手段。三、实战一无限滚动页面Endless page任务描述创建一个无限滚动的页面——当访问者滚动到底部时自动在文本末尾追加当前日期时间让用户总能继续往下滚。这其实就是加载更多消息/更多商品的真实场景简化版。在动手前任务特别强调滚动行为的两个重要特性这直接决定了底部检测的实现方式滚动是弹性的elastic在某些浏览器/设备上我们可以滚动到文档起点或终点的稍外侧底部会出现一段空白随后文档会自动弹回正常位置。滚动是不精确的imprecise当我们滚到页面尽头时实际可能距离文档真实底部还有 050px 的偏差。因此滚动到底部在实现上应理解为访问者距离文档末尾不超过 100px。这正是 官方解答 采用 100px 阈值的由来。检测滚动到底部getBoundingClientRect() 坐标法核心问题是如何判断页面已经滚动到底部官方解答使用窗口相对坐标window-relative coordinates。整个文档被html标签承载即document.documentElement。我们可以用document.documentElement.getBoundingClientRect()拿到整个文档的窗口相对坐标其中bottom属性就是文档底部的窗口相对坐标。举例说明假设整个 HTML 文档高度为2000px窗口高度为600px// 页面位于顶部时 // 文档顶部窗口相对坐标 0 document.documentElement.getBoundingClientRect().top 0 // 文档底部窗口相对坐标 2000文档很长远在窗口下方 document.documentElement.getBoundingClientRect().bottom 2000向下滚动500px后// 文档顶部位于窗口上方 500px 处 document.documentElement.getBoundingClientRect().top -500 // 文档底部靠近了 500px document.documentElement.getBoundingClientRect().bottom 1500一直滚动到尽头窗口高度 600px// 文档顶部位于窗口上方 1400px 处 document.documentElement.getBoundingClientRect().top -1400 // 文档底部正好与窗口底部平齐窗口相对坐标 600 document.documentElement.getBoundingClientRect().bottom 600注意一个关键点bottom永远不可能等于 0——因为文档底部永远不会到达窗口顶部。bottom坐标的最低极限就是窗口高度本例中为 600再往下也无法滚动。窗口高度可以通过document.documentElement.clientHeight获取。于是判断条件就非常清晰当文档底部距离窗口底部不超过 100px即bottom落在600 ~ 700px区间时就认为滚动到了底部。完整的 populate() 实现官方解答 的核心是一个在到达页面底部时不断追加内容真实场景中是加载更多数据的函数function populate() { while(true) { // 文档底部的窗口相对坐标 let windowRelativeBottom document.documentElement.getBoundingClientRect().bottom; // 如果用户还没滚得足够远距底部 100px停止追加 if (windowRelativeBottom document.documentElement.clientHeight 100) break; // 追加更多数据 document.body.insertAdjacentHTML(beforeend, pDate: ${new Date()}/p); } }这段代码的巧妙之处在于使用while(true)循环而非单次if当用户距离底部不足 100px 时追加一批内容后文档变长bottom坐标随之增大如果追加后用户仍然处于底部附近例如内容较短、窗口很高循环会继续追加直到距底部超过 100px 为止。这样可以一次性填满整个视口避免出现追加一点又到边、还要再滚一次的尴尬。在实际页面中把它挂在window.onscroll上并在页面加载时立即调用一次即可// 立即执行一次处理页面刚打开时就处于底部附近的情况 populate(); window.addEventListener(scroll, populate);弹性滚动对阈值的影响由于滚动是弹性的用户可能把页面拉到文档真实底部以下出现空白再弹回。因此不要用scrollHeight与scrollTop clientHeight的严格相等来判断底部也不要把阈值设得太小——官方选择的 100px 正是为了同时容忍弹性滚动和不精确滚动带来的偏差。完整的可运行示例可查看 endless-page 示例源码。四、实战二回到顶部按钮Up/down button任务描述创建一个回到顶部按钮交互规则如下页面滚动距离不足一个窗口高度时按钮不可见页面滚动超过一个窗口高度后左上角出现一个向上的箭头滚回去则消失点击箭头页面平滑或直接滚回顶部。官方解答的完整实现位于 up/down button 示例源码核心逻辑如下div idarrowTop hidden/div script arrowTop.onclick function() { window.scrollTo(pageXOffset, 0); // scrollTo 会触发 scroll 事件箭头会自动隐藏 }; window.addEventListener(scroll, function() { arrowTop.hidden (pageYOffset document.documentElement.clientHeight); }); /script逐行解读显示/隐藏判断pageYOffset已滚过的垂直距离与document.documentElement.clientHeight窗口高度比较。当滚动距离小于窗口高度时hidden true箭头隐藏一旦超过hidden false箭头出现。这正是滚动超过一个窗口高度才显示的实现。回到顶部window.scrollTo(pageXOffset, 0)把窗口滚回y 0处。这里保留pageXOffset是为了保持当前水平滚动位置不变只重置垂直滚动——这是实现只回到顶部、不改变横向位置的正确写法。自动隐藏的巧妙之处scrollTo本身会触发scroll事件因此滚回顶部后监听器会再次执行发现pageYOffset clientHeight箭头自动隐藏——无需在点击处理器里手动隐藏。视觉上示例通过 CSS 实现箭头样式position: fixed; top: 10px; left: 10px;固定定位在左上角::before伪元素内容为▲并给#arrowTop添加cursor: pointer。完整样式可参考 示例源码 中的style块。五、实战三图片懒加载Load visible images任务描述面向低速网络、想节省移动流量的场景。思路是不立即加载真实图片而是先用占位图替代等图片进入视口用户能看到它时再把src换成真实地址。占位图标记约定如下img srcplaceholder.svg width128 height128>function isVisible(elem) { let coords elem.getBoundingClientRect(); let windowHeight document.documentElement.clientHeight; // 元素上边缘是否可见 let topVisible coords.top 0 coords.top windowHeight; // 元素下边缘是否可见 let bottomVisible coords.bottom windowHeight coords.bottom 0; return topVisible || bottomVisible; }coords.top/coords.bottom是元素相对窗口的坐标top在 0 与窗口高度之间说明上边缘露出视口bottom小于窗口高度且大于 0 说明下边缘露出视口。两者任一成立即判定为可见。加载可见图片showVisible()function showVisible() { for (let img of document.querySelectorAll(img)) { let realSrc img.dataset.src; if (!realSrc) continue; // 普通图片无>function isVisible(elem) { let coords elem.getBoundingClientRect(); let windowHeight document.documentElement.clientHeight; // 扩展上下边界各放宽一个窗口高度 let extendedTop -windowHeight; let extendedBottom 2 * windowHeight; // 上边缘可见 || 下边缘可见 let topVisible coords.top extendedTop coords.top extendedBottom; let bottomVisible coords.bottom extendedBottom coords.bottom extendedTop; return topVisible || bottomVisible; }相比基础版这里把判断窗口从[0, windowHeight]扩展为[-windowHeight, 2 * windowHeight]凡是位于当前视口前一屏到后一屏范围内的图片都会被提前加载。这样用户在滚动到达之前图片可能已经缓存完毕浏览体验更平滑——代价是可能加载少量尚未真正进入视口的图片属于流量换体验的取舍。六、总结scroll 事件的核心要点回顾原文档与三个实战任务可以提炼出scroll事件编程的关键规律监听对象有两类window的滚动与可滚动元素的滚动均使用scroll事件滚动位置用pageYOffset/scrollY获取。阻止滚动别用 onscrollscroll事件触发晚于滚动动作preventDefault()无效应在引发滚动的事件如PageUp/PageDown按键上拦截而最可靠的方式是 CSSoverflow属性。滚动到底部要用窗口相对坐标利用document.documentElement.getBoundingClientRect().bottom与clientHeight做比较并保留合理阈值官方取 100px来容忍弹性滚动与不精确滚动。显示/隐藏式 UI 依赖滚动距离判断用pageYOffset与clientHeight比较决定控件显隐程序化滚动如scrollTo会触发scroll事件可借此实现自动隐藏。懒加载的本质是可见性判定 一次性替换用getBoundingClientRect()判断元素是否进入视口加载后清除data-src防止重复加载。滚动相关的尺寸、坐标等底层 API如scrollHeight、clientHeight、getBoundingClientRect的完整语义可继续阅读本教程 文档尺寸与滚动 与 窗口尺寸与滚动 两章三个任务的完整可运行源码与官方解答分别位于 endless-page、updown-button 和 load-visible-img 目录下可直接打开对应的solution.view/index.html体验效果。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐locomotive-scroll性能优化图片懒加载与滚动locomotive scroll性能优化图片懒加载与滚动 你是否遇到过这样的情况网页加载时卡顿严重大量图片一次性加载导致带宽浪费或者滚动时元素动画不流Bifrost三星设备固件下载与管理的跨平台解决方案Bifrost三星设备固件下载与管理的跨平台解决方案 你是否曾为寻找三星设备固件而烦恼Bifrost为你提供了一个优雅的解决方案——这是一个跨平台的 三星固文档/教程前端Whisper-WebUI字幕生成实战从文件、YouTube到麦克风录制Whisper WebUI字幕生成实战从文件、YouTube到麦克风录制 Whisper WebUI是一款基于Whisper模型的字幕生成工具提供直观的WeAI 应用语音音频处理本地部署上一篇把 Lance 数据湖接入 Hudi/Iceberg 架构向量检索延迟压进 1 毫秒的实战指南下一篇Sass 退化颜色Degenerate Colors规范NaN、无穷大与负零在颜色通道中的审查机制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门