利用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 += ` 
`; itemArray.forEach(item => { str += `
111
111
`; }) str += `
`; }) 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();

  参考

  IntersectionObserver API 使用教程

  intersectionObserver WebApiI

   

js