利用IntersectionObserver完成懒加载、加载更多
IntersectionObserver (交叉观察器)
IntersectionObserver
提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。
IntersectionObserver
对象被创建以后,其被配置为监听根中一段给定比例的可见区域。一旦IntersectionObserver被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;可以在同一个观察者对象中配置监听多个目标元素。
var obj = new IntersectionObserver(callback, options); // 开始监听 element为单个dom元素,多个元素都需要监听 需要多次调用 同一个观察者对象中配置监听多个目标元素。 obj.observe(element); // 停止监听 obj.unobserve(element); // 销毁监听 obj.disconnect(); // 返回所有观察目标的对象数组 每个对象包含目标元素与根每次的相交信息。 obj.takeRecords();
callback回调函数中,参数是每一个监听的对象
var obj = new IntersectionObserver(changes => {
//changes是一个数组,数组中每个元素都是监听的对象,每个对象包含很多信息,提取出需要的进行判断
changes.forEach(item => {
let {
isIntersecting,
target
} = item;
if (isIntersecting) {
lazyImgView(target);
//满足条件后 停止监听
obj.unobserve(target);
}
})
}, options);
每个对象包含8个属性
time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
target:被观察的目标元素,是一个 DOM 节点对象
rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
boundingClientRect:目标元素的矩形区域的信息
intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
isIntersecting是用来判断元素是否符合options中的可见条件
例如:
var options = {
threshold: [0.7]
}
var obj = new IntersectionObserver(changes => {
changes.forEach(item => {
console.log(item)
let {
isIntersecting,
target
} = item;
//target 出现70%,isIntersecting为true
if (isIntersecting) {
lazyImgView(target);
obj.unobserve(target);
}
})
}, options);
接着看一下options
这个 options 配置对象,有三个属性 threshold,root,rootMargin
threshold,他是一个数组
配置为 threshold: [0, 0.25, 0.5, 0.75, 1]
这个表示目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数
root,
监听元素的祖先元素Element
对象,其边界盒将被视作视口。
目标在根的可见区域的的任何不可见部分都会被视为不可见。
rootMargin.
一个在计算交叉值时添加至根的边界盒中的一组偏移量
可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和CSS 中的margin
属性等同,默认值是"0px 0px 0px 0px" (字符串) 该属性还没有正式使用
在交叉检测开始之前,由rootMargin规定的矩形的每一边都会被添加至
root
元素的边框盒的相应边。例如,可以让你向外调整边界,使得目标元素被认为是100%可见的,即使此元素得一部分长或宽被裁剪,或者在边缘过于靠近根边框盒边界的情况下,将目标视为部分隐藏。
浏览器兼容:
html:
效果:
(为了能够清晰的分辨 加载更多的出现,每次请求的数据和新数据之间有一个空,所以能够看到有一个空缺)
完整代码:
var imgListMoudule = (function () { // 获取操作得dom元素 var container = $('#imgList'), page = 1, supportNumber = 0; // 获取数据 分页加载 var queryData = (page) => { //假数据 data = [{ imgurl: "img/img.png", id: "1" }, { imgurl: "img/img.png", id: "2" }, { imgurl: "img/img.png", id: "3" }, { imgurl: "img/img.png", id: "4" }, { imgurl: "img/img.png", id: "5" }, { imgurl: "img/img.png", id: "6" }, { imgurl: "img/img.png", id: "7" }, { imgurl: "img/img.png", id: "8" }, { imgurl: "img/img.png", id: "9" }] bindHtml(data); // $.ajax({ // type: "POST", // url: "", // data: "", // async: true, // success: function (data) { // data = [{ // imgurl: "", // id: "1" // }] if (page > 3) { // 数据没有了 将加载更多的元素去掉 不会再触发加载更多 $('.load_more').hide(); } bindHtml(data); ++page; // }, // error: function () { // console.log("提交失败!"); // } // }); } // 页面渲染 var bindHtml = data => { var result = []; for (var i = 0; i < data.length; i += 2) { result.push(data.slice(i, i + 2)); } var str = ""; result.forEach(itemArray => { str += ` <div class="img_list_box" lazyload>`; itemArray.forEach(item => { str += `<a href="javascript:void(0)" class="img_item" data-id="${item.id}"> <div class="img_box" data-id="${item.id}"> <img src="" alt="" class="index_img" data-realUrl="${item.imgurl}"> </div> <div class="img_text"> <div class="look_box img_text_item"> <span>111</span> <img src="img/look_icon.png" alt=""> </div> <div class="zan_box img_text_item"> <span>111</span> <img src="img/zan_icon.png" alt="" class="zan_btn"> </div> </div> </a>`; }) str += ` </div>`; }) container.append(str); } // 懒加载监听 var lazyLoad = () => { var options = { threshold: [0.7] } var obj = new IntersectionObserver(changes => { changes.forEach(item => { let { isIntersecting, target } = item; if (isIntersecting) { lazyImgView(target); obj.unobserve(target); } }) }, options); // 获取需要懒加载的元素 只获取设置了lazyload属性得元素 var imgBox = $('.img_list_box[lazyload]'); [].forEach.call(imgBox, lazyImg => { obj.observe(lazyImg); }) } // 懒加载渲染 var lazyImgView = item => { var imgs = $(item).find('.img_box img'); // 防止之前数据重新渲染,移除掉已经监听得 item.removeAttribute('lazyload'); [].forEach.call(imgs, img => { img.src = img.getAttribute('data-realUrl'); img.onload = () => { img.style.opacity = 1; } }) } // 加载更多 var loadMore = () => { var options = { threshold: [0.5] } var loadMoreDom = document.getElementsByClassName('load_more')[0]; if (!loadMoreDom) { return false; } var obj = new IntersectionObserver(changes => { if (changes[0].isIntersecting) { queryData(page); lazyLoad(); } }, options); obj.observe(loadMoreDom); } return { async init() { queryData(page); lazyLoad(); loadMore(); } } })(); imgListMoudule.init();
参考