背景:
由于网页的执行都是单线程的,在JS执行的过程中,页面会呈现阻塞状态。因此,如果JS处理的数据量过大,过程复杂,可能会造成页面的卡顿。传统的数据展现都以分页的形式,但是分页的效果并不好,需要用户手动点击下一页,才能看到更多的内容。有很多网站使用无限分页的模式,即网页视窗到达内容底部就自动加载下一部分的内容...
原理:
实现无限分页的过程大致如下:
1 视窗滚动到底部;
2 触发加载,添加到现有内容的后面。
因此,可能会出现两种情况:
1 当页面的内容很少,没有出现滚动条;触发加载页面事件,直到加载到满足条件时停止加载;
2 当页面的内容很多,出现了滚动条
复制代码
先说第一种传统的方法
需要理解的概念
scrollHeight即真实内容的高度;
clientHeight比较好理解,是视窗的高度,就是我们在浏览器中所能看到内容的高度;
scrollTop是视窗上面隐藏掉的部分。
实现思路:
1 如果真实的内容比视窗高度小,则一直加载到超过视窗
2 如果超过了视窗,则判断下面隐藏的部分的距离是否小于一定的值,如果是,则触发加载。(即滚动到了底部)
// 触发条件函数
function lowEnough(){
var pageHeight = Math.max(document.body.scrollHeight,document.body.offsetHeight);
var viewportHeight = window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight || 0;
var scrollHeight = window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop || 0;
return pageHeight - viewportHeight - scrollHeight < 20; // 通过 真实内容高度 - 视窗高度 - 上面隐藏的高度 < 20,作为加载的触发条件
}
复制代码
第二种方法:IntersectionObserver(简单、但对浏览器有要求)
一、API
它的用法非常简单。
var io = new IntersectionObserver(callback, option);
// 开始观察
io.observe(document.getElementById('example'));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
复制代码
上面代码中,IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。
构造函数的返回值是一个观察器实例。实例的observe方法可以指定观察哪个 DOM 节点。
二、callback 参数
目标元素的可见性变化时,就会调用观察器的回调函数callback。
callback一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。
var io = new IntersectionObserver(
entries => {
console.log(entries);
}
);
复制代码
上面代码中,回调函数采用的是箭头函数的写法。callback函数的参数(entries)是一个数组,每个成员都是一个IntersectionObserverEntry对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。
三、IntersectionObserverEntry 对象
IntersectionObserverEntry对象提供目标元素的信息,一共有六个属性。
每个属性的含义如下。
time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
target:被观察的目标元素,是一个 DOM 节点对象
rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
boundingClientRect:目标元素的矩形区域的信息
intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
复制代码
上图中,灰色的水平方框代表视口,深红色的区域代表四个被观察的目标元素。它们各自的intersectionRatio图中都已经注明。
实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.img-area{width: 500px;height: 500px; margin: 0 auto;}
.my-photo{width:500px; height: 300px}
</style>
</head>
<body>
<div id="container">
<div class="img-area"><img class="my-photo" alt="loading" src="./img/1.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" src="./img/2.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" src="./img/3.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" src="./img/4.jpg" /></div>
</div>
<div class="scrollerFooter1">
没有内容了
</div>
<script>
function infinityScroll(footerNode, callback) {
var observer = new IntersectionObserver(function(changes) {
// 注意intersectionRatio这个属性值的判断
if (changes[0].intersectionRatio <= 0) return;
callback();
});
observer.observe(document.querySelector(footerNode));
}
infinityScroll('.scrollerFooter1', function() {
for (var i = 0; i< 3; i++) {
document.getElementById('container').appendChild(document.getElementById('container').firstChild)
}
});
</script>
</body>
</html>
复制代码
当然也可以实现懒加载:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.img-area{width: 500px;height: 500px; margin: 0 auto;}
.my-photo{width:500px; height: 300px}
</style>
</head>
<body>
<div class="container">
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/1.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/2.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/3.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/4.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/5.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/1.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/2.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/3.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/4.jpg" /></div>
<div class="img-area"><img class="my-photo" alt="loading" data-src="./img/5.jpg" /></div>
</div>
<script>
function lazyLoad(imgClassName) {
const imgList = Array.from(document.querySelectorAll(imgClassName));
var io = new IntersectionObserver(function (ioes) {
ioes.forEach(function (ioe) {
var el = ioe.target;
var intersectionRatio = ioe.intersectionRatio;
if (intersectionRatio > 0 && intersectionRatio <= 1) {
if (!el.src) {
el.src = el.dataset.src
}
}
})
});
imgList.forEach(function(item) {
io.observe(item)
});
}
lazyLoad('.my-photo');
</script>
</body>
</html>
复制代码