为什么要图片懒加载?
进入某个页面时,会有很多图片,有些图片是在下面的,当我们没有滑动到下面时,那些图片还不需要加载的。如果我们加载了也是白加载,这样只会降低网页的加载速度。所以我们需要懒加载,只有滑动到可视区域才会加载当前需要的图片。
实现原理
图片的加载是由src的值引起的,当对src赋值时浏览器就会请求图片资源。我们可以使用html5的自定义属性data-xxx(data-src)来保存真实的路径,当需要加载时(到达可视区域),才将data-xxx的值赋给src
- 图片还没在可视区域时(data-xxx保存真实路径)
- 图片在可视区域时(将data-xxx的值赋予src)
如何判断是否在可视区域
图片距离视窗顶部的距离(getBoundingClientRect().top)小于窗口显示区的高度(window.innerHeight),就在可视区域,如下图蓝色图片。
具体实现代码
<body>
<div>你好你好</div>
<div>你好你好</div>
<div>你好你好</div>
<div>你好你好</div>
<div>你好你好</div>
<div>你好你好</div>
<img style="margin-left: 30%;margin-top: 50px;margin-bottom: 50px;"
data-src="1.webp" alt="">
<img style="margin-left: 30%;margin-top: 50px;margin-bottom: 50px;"
data-src="2.webp" alt="">
<img style="margin-left: 30%;margin-top: 50px;margin-bottom: 50px;"
data-src="3.webp"
alt="">
<div>你好你好</div>
<div>你好你好</div>
<div>你好你好</div>
<div>你好你好</div>
</body>
<style>
div {
height: 350px;
font-size: 50px;
margin: 0 30%;
}
</style>
Script部分
事件监听scroll滚动事件,获取每张图片距离视窗顶部的距离(设置为a)和窗口显示区的高度(设置为b)
如果a<b, 就在可视区域内,就将data_src的值赋予src
这样就可以实现图片懒加载。
const images = document.querySelectorAll('img');
window.addEventListener('scroll', (e) => {
images.forEach(image => {
const imageTop = image.getBoundingClientRect().top;
if (imageTop < window.innerHeight) {
const data_src = image.getAttribute('data-src');
image.setAttribute('src', data_src);
console.log('scroll触发了')
}
});
})
上面的代码,滚动事件会被触发很多次,而且图片已经加载了还是会触发滚动事件,非常消耗资源
最佳解决方法
因此,目前最推荐使用的还是IntersectionObserver
IntersectionObserver是浏览器提供的构造函数(有些浏览器不支持)
IntersectionObserver实例有两个方法,observe(观察一个节点)和unobserve(解除观察)
实例传入一个参数,是回调函数,目标元素(图片)看见了触发一次,看不见也触发一次
这个回调函数接收一个数组参数,数组的每一项都有isIntersecting属性,代表是否在可视区域;而target代表着目标元素
所以当isIntersecting为true时,就将这个元素的data_src的值赋予src
然后再解除对这张图片的观察,事件只会被触发3次
代码如下:
<script>
// 使用IntersectionObserver构造函数
// IntersectionObserver实例有两个方法,observe(观察一个节点)和unobserve(解除观察)
const images = document.querySelectorAll('img');
const callback = (entries) => {
entries.forEach(entry => {
// 每个entry数组都有一个isIntersecting属性(是否在可视区域)
if (entry.isIntersecting) {
// target代表着目标元素
const image = entry.target;
const data_src = image.getAttribute('data-src');
image.setAttribute('src', data_src);
observer.unobserve(image);
console.log('触发了')
}
})
}
const observer = new IntersectionObserver(callback);
images.forEach(image => {
observer.observe(image);
})
</script>