浅析浏览器监听元素是否可见的API:IntersectionObserver介绍及如何使用、应用场景(懒加载、无线滚动)

  在移动端,有个很重要的概念,叫做懒加载,适用于一些图片资源特别多,ajax数据特别多的页面中,经常会有动态加载数据的场景中,这个时候,我们通常是使用监听scroll或者使用setInterval来判断,元素是否进入视图,其中scroll由于其特别大的计算量,会有性能问题,而setInterval由于其有间歇期,也会出现体验问题。
  目前有一个新的 IntersectionObserver API,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。

一、如何使用

var io = new IntersectionObserver(callback, option);

  上面代码中,IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。

  构造函数的返回值是一个观察器实例。实例的observe方法可以指定观察哪个 DOM 节点

// 开始观察
io.observe(document.getElementById('example'));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();

二、callback 参数

  目标元素的可见性变化时,就会调用观察器的回调函数callback

  callback一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。

    var io = new IntersectionObserver(
      entries => {
        console.log(entries);
      }
    );

  上面代码中,回调函数采用的是箭头函数的写法。callback函数的参数(entries)是一个数组,每个成员都是一个IntersectionObserverEntry对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。

三、IntersectionObserverEntry 对象

  IntersectionObserverEntry对象提供目标元素的信息,一共有六个属性。

    {
      time: 3893.92,
      rootBounds: ClientRect {
        bottom: 920,
        height: 1024,
        left: 0,
        right: 1024,
        top: 0,
        width: 920
      },
      boundingClientRect: ClientRect {
         // ...
      },
      intersectionRect: ClientRect {
        // ...
      },
      intersectionRatio: 0.54,
      target: element
    }

  每个属性的含义如下。

  time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒

  target:被观察的目标元素,是一个 DOM 节点对象

  rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null

  boundingClientRect:目标元素的矩形区域的信息

  intersectionRect:目标元素与视口(或根元素)的交叉区域的信息

  intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0

四、应用场景

1、懒加载

  有时,我们希望某些静态资源(比如图片),只有用户向下滚动,它们进入视口时才加载,这样可以节省带宽,提高网页性能。这就叫做"惰性加载"。

  有了 IntersectionObserver API,实现起来就很容易了。

    function query(selector) {
      return Array.from(document.querySelectorAll(selector));
    }

    var observer = new IntersectionObserver(
      function(changes) {
        changes.forEach(function(change) {
          var container = change.target;
          var content = container.querySelector('template').content;
          container.appendChild(content);
          observer.unobserve(container);
        });
      }
    );

    query('.lazy-loaded').forEach(function (item) {
      observer.observe(item);
    });

  上面代码中,只有目标区域可见时,才会将模板内容插入真实 DOM,从而引发静态资源的加载。

2、无限滚动(infinite scroll)的实现也很简单。

    var intersectionObserver = new IntersectionObserver(
      function (entries) {
        // 如果不可见,就返回
        if (entries[0].intersectionRatio <= 0) return;
        loadItems(10);
        console.log('Loaded new items');
      });

    // 开始观察
    intersectionObserver.observe(
      document.querySelector('.scrollerFooter')
    );

五、Option 对象

  IntersectionObserver构造函数的第二个参数是一个配置对象。它可以设置以下属性。

1、threshold属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。

2、root 属性,rootMargin 属性

  很多时候,目标元素不仅会随着窗口滚动,还会在容器里面滚动(比如在iframe窗口里滚动)。容器内滚动也会影响目标元素的可见性,IntersectionObserver API 支持容器内滚动。root属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。

六、注意点

  IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。

  规格写明,IntersectionObserver的实现,应该采用requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。

目前还没怎么用过,学习记录下,学习文章:https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
posted @ 2018-03-20 22:29  古兰精  阅读(749)  评论(0编辑  收藏  举报