方案一:clientHeight、scrollTop 和 offsetTop
首先给图片一个占位资源:
==注意,要设置图片的width,height才可以获取到offsetTop==
<img src="default.jpg" data-src="http://www.xxx.com/target.jpg" />
接着,通过监听 scroll 事件来判断图片是否到达视口:
let imgs = document.getElementsByTagName("img");
let num = imgs.length;
let count = 0;//计数器,从第一张图片开始计,避免每次都要去遍历所有图片
lazyload();//还未滑动时加载一波图片
window.addEventListener('scroll', lazyload);
function lazyload() {
let viewHeight = document.documentElement.clientHeight;//视口高度
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;//滚动条的高度
for(let i = count; i <num; i++) {
// 如果滚动高度加视口高度大于元素距离顶部的高度,即元素现在已经出现在视口中
if(imgs[i].offsetTop < scrollTop + viewHeight) {
if(imgs[i].getAttribute("src") !== "default.jpg") continue;
imgs[i].src = imgs[i].getAttribute("data-src");
count ++;
}
}
}
然后来个节流函数优化,避免频繁触发
function throttle(fn, interval) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, interval);
};
}
window.addEventListener('scroll', throttle(lazyload, 200));
方案二:getBoundingClientRect()
function lazyload() {
for(let i = count; i <num; i++) {
// 元素现在已经出现在视口中
if(imgs[i].getBoundingClientRect().top < document.documentElement.clientHeight) {
if(imgs[i].getAttribute("src") !== "default.jpg") continue;
imgs[i].src = imgs[i].getAttribute("data-src");
count ++;
}
}
}
方案三:IntersectionObserver接口
IntersectionObserver接口可用于资源的预加载。监听、判断、节流一次搞定。但是兼容性不好,仅提供思路参考。
概念
IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种可以异步==监听目标元素与其祖先或视窗(viewport)交叉状态==的手段。祖先元素与视窗(viewport)被称为根(root)。
交叉就是说明元素在视窗里面了
API:
var io = new IntersectionObserver(callback, options)
以上代码会返回一个IntersectionObserver实例,callback是当元素的可见性变化时候的回调函数,options是一些配置项(可选)。
callback函数有个entries参数,它是个IntersectionObserverEntry对象数组,包含了很多当前被observe的el的属性包括:
- isIntersecting 目标元素当前是否可见 Boolean值
- target 观察的目标元素
其他API:
io.observe(el) 开始观察,接受一个DOM节点对象
io.unobserve(el) 停止观察 接受一个element元素
io.disconnect() 关闭观察器
懒加载方案:
let imgs = document.getElementsByTagName("img");
const observer = new IntersectionObserver(entries => {
//entries 是被观察的元素集合
for(let i = 0, len = entries.length; i < len; i++) {
let entry = entries[i];
// 通过这个属性判断是否在视口中
if(entry.isIntersecting) {
const imgElement = entry.target;
imgElement.src = imgElement.getAttribute("data-src");
observer.unobserve(imgElement);
}
}
})
Array.from(imgs).forEach(item => observer.observe(item));
方案一:clientHeight、scrollTop 和 offsetTop
首先给图片一个占位资源:
==注意,要设置图片的width,height才可以获取到offsetTop==
接着,通过监听 scroll 事件来判断图片是否到达视口:
然后来个节流函数优化,避免频繁触发
方案二:getBoundingClientRect()
方案三:IntersectionObserver接口
IntersectionObserver接口可用于资源的预加载。监听、判断、节流一次搞定。但是兼容性不好,仅提供思路参考。
概念
IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种可以异步==监听目标元素与其祖先或视窗(viewport)交叉状态==的手段。祖先元素与视窗(viewport)被称为根(root)。
交叉就是说明元素在视窗里面了
API:
以上代码会返回一个IntersectionObserver实例,callback是当元素的可见性变化时候的回调函数,options是一些配置项(可选)。
callback函数有个entries参数,它是个IntersectionObserverEntry对象数组,包含了很多当前被observe的el的属性包括:
其他API:
懒加载方案: