什么是移动端滚动分页
当用户滑动页面到底部时,便会触发页面的加载分页数据功能
解决方案
解决方案
目前主流的解决方案主要有两个,scroll 和 IntersectionObserver
- scroll 是页面滚动事件,当页面滚动时,判断滚动条距离是否触底,如果是,便执行分页逻辑
- IntersectionObserver 是一个用于观察元素可见性变化的API。它可以用于检测元素是否进入或离开视口(viewport),或者与其他元素发生交叉
scroll
目前m端淘宝采用的是 scroll,它的特点是兼容性够好。几乎全部的浏览器都支持常用,缺点便是事件触发太频繁,因为每一滚动滚动都需要进行判断。
当我们移除掉淘宝 body元素上的scroll事件时,分页逻辑便失效了。
如果我们自己利用 scroll事件,实现一个分页事件,也是不难的。主要思路如下:
提供的示例代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } li { height: 50px; border: 1px solid #000; } p { height: 100px; background-color: sandybrown; } .loading::before { background-color: rgba(0, 0, 0, .6); position: fixed; width: 100vw; height: 100vh; z-index: 0; content: ""; } .loading::after { content: "加载中..."; font-size: 30px; color: #fff; width: 400px; height: 100px; display: flex; align-items: center; justify-content: center; border-radius: 50px; position: fixed; top: 50%; left: 50%; background-color: seagreen; transform: translate(-50%, -50%); } body { height: 100vh; overflow: auto; background-image: linear-gradient(yellow, pink, skyblue); } </style> </head> <body> <ul></ul> <p>加载中</p> <script> const p = document.querySelector("p"); // 获取第一个 <p> 元素 const ul = document.querySelector("ul"); // 获取第一个 <ul> 元素 loadData(); // 调用 loadData 函数加载数据 window.addEventListener("scroll", function () { const { clientHeight } = document.body; // 获取页面可视区域的高度 const { scrollTop } = document.documentElement; // 获取页面滚动的垂直距离 const { scrollHeight } = document.documentElement; // 获取页面的总高度 if (scrollHeight - scrollTop - clientHeight === 0) { // 如果滚动到底部 loadData(); // 调用 loadData 函数加载数据 } }); async function loadData() { if (document.body.classList.contains("loading")) { // 如果页面正在加载数据,则返回 return; } console.log("开始分页"); document.body.classList.add("loading"); // 给 body 元素添加 loading 类,表示正在加载数据 await sleep(2000); // 等待 2000 毫秒 let str = ""; for (let index = ul.childElementCount; index < ul.childElementCount + 100; index++) { // 生成 li 元素的内容 str += `<li>${index + 1}</li>`; } ul.innerHTML += str; // 将生成的 li 元素添加到 ul 元素中 document.body.classList.remove("loading"); // 移除 body 元素的 loading 类,表示数据加载完成 } function sleep(time, fn) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time); }); } </script> </body> </html>
效果如下
IntersectionObserve
IntersectionObserver 是一个 JavaScript API,用于异步监测目标元素与其祖先或视口之间的交叉状态。通过使用 IntersectionObserver,可以轻松地检测目标元素是否进入或离开视口,或者与其祖先元素交叉的程度。
使用 IntersectionObserver 的好处是它可以异步地观察元素的交叉状态,而不会导致性能问题。缺点是兼容性没有scroll 好,但是主流浏览器也支持了。
以下是它的基本代码
// 创建一个 IntersectionObserver 实例 const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { // 目标元素进入视口 console.log('目标元素进入视口'); } else { // 目标元素离开视口 console.log('目标元素离开视口'); } }); }); // 监听目标元素 const targetElement = document.querySelector('#target'); observer.observe(targetElement);
目前vant4中的Lazyload 懒加载组件底层使用的技术是 IntersectionObserve
如果我们要利用 IntersectionObserve 实现一个自己的分页事件,也是挺便捷的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } li { height: 50px; border: 1px solid #000; } p { height: 100px; background-color: sandybrown; } .loading::before { background-color: rgba(0, 0, 0, .6); position: fixed; width: 100vw; height: 100vh; z-index: 0; content: ""; } .loading::after { content: "加载中..."; font-size: 30px; color: #fff; width: 400px; height: 100px; display: flex; align-items: center; justify-content: center; border-radius: 50px; position: fixed; top: 50%; left: 50%; background-color: seagreen; transform: translate(-50%, -50%); } body { height: 100vh; overflow: auto; background-image: linear-gradient(yellow, pink, skyblue); } </style> </head> <body> <ul></ul> <p>加载中</p> <script> // 选择第一个 <p> 元素 const p = document.querySelector("p"); // 选择第一个 <ul> 元素 const ul = document.querySelector("ul"); // 创建一个 IntersectionObserver 对象 const ob = new IntersectionObserver(function (entries) { // 获取第一个观察目标的 isIntersecting 属性 const isIntersecting = entries.shift().isIntersecting; // 如果目标元素进入视口,执行 loadData() 函数 isIntersecting && loadData(); }, { // 设置触发回调函数的阈值为 0.1 threshold: 0.1 }); // 将 <p> 元素添加到 IntersectionObserver 中进行观察 ob.observe(p); async function loadData() { if (document.body.classList.contains("loading")) { return } console.log("开始分页"); document.body.classList.add("loading") await sleep(2000) let str = ""; for (let index = ul.childElementCount; index < ul.childElementCount + 100; index++) { str += `<li>${index + 1}</li>` } ul.innerHTML += str document.body.classList.remove("loading") } function sleep(time, fn) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time); }) } </script> </body> </html>
效果如下
总结
目前主流的解决方案主要有两个,scroll 和 IntersectionObserver
- scroll 是页面滚动事件,当页面滚动时,判断滚动条距离是否触底,如果是,便执行分页逻辑 优点是兼容性好,缺点是事件触发频繁,性能差
- IntersectionObserver 是一个用于观察元素可见性变化的API。它可以用于检测元素是否进入或离开视口(viewport),或者与其他元素发生交叉。优点时性能好,缺点是兼容性比 scroll 稍差。