懒加载,通常是延迟加载初始视口外的图像,直到我们滚动页面,达到图像与底部视口的交汇处才开始加载图像。
我们需要做的是判断图片是否出现在当前视口,然后控制图片的加载。
有几种实现方式:
-
scroll
和位置计算 getBoundingClientRect API
IntersectionObserver API
-
loading
属性 - 第三方库(无非是上述方法的实现)
scroll 和位置计算
我们需要使用 clientTop
、offsetTop
、clientHeight
和 scrollTop
计算图片位置。
关于这些属性的相关内容请查看元素大小和滚动。
大致的代码如下:
let imgs = document.querySelectorAll('img')
function lazyload() {
const clientH = document.documentElement.clientHeight
const clientT = document.documentElement.scrollTop || document.body.scrollTop
for (let i = 0; i < imgs.length; i++) {
if (clientH + clientT > imgs[i].offsetTop && !imgs[i].src) {
imgs[i].src = imgs[i].getAttribute('data-src')
}
}
}
通过监听 window.scroll
滚动事件,滚动时计算图片与视口的位置,为图片的 src
赋值即可。
doucment.addEventListener('scroll', () => {
// ...
})
需要考虑🤔的问题,我们每滚动一次页面都会触发多次次 scroll
滚动事件及其事件处理程序。为了性能考虑,我们为其加一个节流器,这里我们使用 Lodash 库的 _.throttle()
方便快速开发。
_.throttle(func, [(wait = 0)], [(options = {})])
getBoundingClientRect API
为了不那么麻烦的计算元素的位置,浏览器为我们提供了新的 getBoundingClientRect API
。
Element.getBoundingClientRect()
方法返回元素的大小及其相对于视口的位置。
👇下图来自 MDN:
通过 Element.getBoundingClientRect()
获取元素相对于浏览器的 top
值,与当前可视区高度进行对比(通过 document.body.clientHeight
获取),如果小于可视区高度,说明图片进入可视范围,可以加载图像了。
const viewHeight = document.body.clientHeight
const rect = item.getBoundingClientRect()
if (rect.bottom >= 0 && rect.top < viewHeight) { ... }
IntersectionObserver API
Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。
创建一个 observer
后需要给定一个目标元素进行观察。
const target = document.querySelector('.list')
observer.observe(target)
只要目标满足为 IntersectionObserver 指定的阈值,就会调用回调。回调接收 IntersectionObserverEntry
对象和观察者的列表:
const callback = (entries, observer) => {
entries.forEach(entry => {
// 每个条目描述了一个观察到的目标元素的交点变化:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
})
}
其中,entry.isIntersecting
代表目标元素可见:
const observer = new IntersectionObserver((target) => {
// target 为目标元素集合
target.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
observer.unobserve(img) // 解除观察
}
})
})
observer.observe(img)
loading 属性
只需要为 img
加上 loading=lazy
:
<img src="/path/to/pic.jpg" loading="lazy" />
使用时,考虑兼容情况。
详细内容查看:
第三方库
一些第三方库的选择: