当用户滑动页面到底部时,便会触发页面的加载分页数据功能
解决方案
目前主流的解决方案主要有两个,scroll 和 IntersectionObserver
目前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>
效果如下
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