<img src="占位图.png">图片再设置一个自定义属性,值为真实的图片地址:
<img src="占位图.png" data-src="https://k.sinaimg.cn/n/news/transform/20171113/puY7-fynship2141885.jpg/w700d1q75cms.jpg?by=cms_fixed_width">然后在「合适的时机」把data-src的值赋给src,让真实的图片显示,这就是图片「懒加载」的基本原理。那什么时候是“合适的时机”呢,接着往下看。
window.addEventListener("scroll", () => {})
当img标签的顶部到可视区域顶部的距离,小于可视区域高度时,我们就认为图片进入了可视区域,画张图表示:红色框表示浏览器可视区域,蓝色框表示图片元素
就得到类似如下代码:
window.addEventListener("scroll", () => { // 堆代码 duidaima.com const img = document.querySelectorAll('img') img.forEach(img => { const rect = img.getBoundingClientRect(); console.log("rect", rect); if (rect.top < document.body.clientHeight) { img.src = img.dataset.src } }) })
Element.getBoundingClientRect() 方法返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。
这样虽然也能实现,但可以看到,这样需要一直在监听页面滚动,而且触发的非常频繁,非常耗费性能,为了解决这一点,还需要在另外加防抖函数。并且还要计算滚动位置,很麻烦。
Intersection Observer API
这个api是浏览器原生自带的,用于异步检测目标元素与父级或顶级元素是否产生交叉。
2.一个用于自定义配置什么情况下才会产生交叉的对象
let observer = new IntersectionObserver( () => { console.log('交叉了') }, { root: null, rootMargin: "0px 0px 0px 0px", threshold: 0.5 } ); // 获取所有的图片元素 const imgs = document.querySelectorAll("img"); // 遍历这些元素 imgs.forEach((img) => { // 观察这些元素 observer.observe(img); });先看第二个参数,是一个对象,有三个属性,这三个属性可以都不传,但必须传个对象,可以是空对象。
rootMargin: 表示root元素的偏移量,怎么理解呢,正常是目标元素与root元素本身所在的位置交叉时触发回调函数,rootMargin可以改变与root元素交叉时的位置,这里必须用张图来表示了。
红色区域表示root元素,蓝色表示图片,如果rootMargin传了"10px 10px 10px 10px",当图片与往外偏移10px的root元素产生交叉时就会触发回调。这个参数还可以传负数,如果传了"-10px -10px -10px -10px",就是往内偏移。这个用到的应该不多吧!
threshold:是一个0~1之间的值,表示一个触发的阈值,如果是0,只要目标元素一碰到root元素,就会触发,如果是1,表示目标元素完全进入root元素范围,才会触发。let observer = new IntersectionObserver( (entries) => { console.log('交叉了') console.log(entries) }, { root: null, rootMargin: "0px 0px 0px 0px", threshold: 0.5 } ); // 获取所有的图片元素 const imgs = document.querySelectorAll("img"); // 遍历这些元素 imgs.forEach((img) => { // 用observe方法观察这些元素 observer.observe(img); });
let observer = new IntersectionObserver( (entries) => { console.log("交叉了"); console.log(entries); for (const entrie of entries) { if (entrie.isIntersecting) { const img = entrie.target; img.src = img.dataset.src; observer.unobserve(img); } } }, { root: null, rootMargin: "0px 0px 0px 0px", threshold: 0.5 } ); // 获取所有的图片元素 const imgs = document.querySelectorAll("img"); // 遍历这些元素 imgs.forEach((img) => { // 用observe方法观察这些元素 observer.observe(img); });当产生交叉时,把img的data-src赋值给src,就让真实的图片显示了,然后再通过observer实例提供的unobserve方法,取消对该元素的观察。到这里我们的图片懒加载就完成了,这样做的好处是:
2.用起来简单,不用计算滚动距离。
看效果
可以看到,我们用到的IntersectionObserver构造函数,以及几个必须用的属性和方法,都是完全支持的。完全不必担心低版本浏览器。