深入理解 Intersection Observer

5,376 阅读4分钟

什么是 Intersection Observer

Intersection Observer API 提供给web开发者,一种异步查询元素相对于其他元素或窗口位置的能力。它常用于追踪一个元素在窗口的可视问题,比如下图,滚动页面,顶部会提示绿色方块的可视性。

在 Intersection Observer 出来之前,传统位置计算的方式,依赖于对DOM状态的轮询计算,然而这种方式会在主线程里密集执行从而造成页面性能问题,getBoundingClientRect()的频繁调用也可能引发浏览器的样式重计算和布局。如果是在iframe里,因为同源策略,我们不能直接访问元素,也就很难用传统方式去处理iframe里的元素。

Intersection Observer 的设计,就是为了更方便的处理元素的可视问题。使用 Intersection Observer 我们可以很容易的监控元素进入和离开可视窗口,实现节点的预加载和延迟加载。Intersection Observer 并不是基于像素变化的实时计算,它的反馈会有一定的延时,这种异步的方式减少了对DOM和style查询的昂贵计算和持续轮询,相比传统方式降低了CPU、GPU的消耗。

Intersection Observer 使用方法

// 定义相交监视器
var observer = new IntersectionObserver(changes => {
  for (const change of changes) {
    console.log(change.time);               // 发生变化的时间
    console.log(change.rootBounds);         // 根元素的矩形区域的信息
    console.log(change.boundingClientRect); // 目标元素的矩形区域的信息
    console.log(change.intersectionRect);   // 目标元素与视口(或根元素)的交叉区域的信息
    console.log(change.intersectionRatio);  // 目标元素与视口(或根元素)的相交比例
    console.log(change.target);             // 被观察的目标元素
  }
}, {});

// 开始观察某个目标元素
observer.observe(target);

// 停止观察某个目标元素
observer.unobserve(target);

// 关闭监视器
observer.disconnect();

1. IntersectionObserver Callback

当目标元素和根元素相交发生改变时,会触发监视器的回调。

callback IntersectionObserverCallback = void (
    sequence<IntersectionObserverEntry> entries, 
    IntersectionObserver observer
);

回调的参数,entries 是一个IntersectionObserverEntry数组,数组的长度是我们监控目标元素的个数,另一个参数是 IntersectionObserver 对象。

2. IntersectionObserver Entry

IntersectionObserverEntry 对象提供了目标元素与跟元素相交的详细信息。他有如下几个属性。

interface IntersectionObserverEntry {
  readonly attribute DOMHighResTimeStamp time;
  readonly attribute DOMRectReadOnly? rootBounds;
  readonly attribute DOMRectReadOnly boundingClientRect;
  readonly attribute DOMRectReadOnly intersectionRect;
  readonly attribute boolean isIntersecting;
  readonly attribute double intersectionRatio;
  readonly attribute Element target;
};

  1. time:发生相交到相应的时间,毫秒。
  2. rootBounds:根元素矩形区域的信息,如果没有设置根元素则返回null,图中蓝色部分区域。
  3. boundingClientRect:目标元素的矩形区域的信息,图中黑色边框的区域。
  4. intersectionRect:目标元素与视口(或根元素)的交叉区域的信息,图中蓝色方块和粉红色方块相交的区域。
  5. isIntersecting:目标元素与根元素是否相交
  6. intersectionRatio:目标元素与视口(或根元素)的相交比例。
  7. target:目标元素,图中黑色边框的部分。

3. IntersectionObserver Option

IntersectionObserver Option 是 IntersectionObserver 构造函数的第二个参数,用来配置监视器的部分信息。

dictionary IntersectionObserverInit {
  Element?  root = null;
  DOMString rootMargin = "0px";
  (double or sequence<double>) threshold = 0;
};
  1. root:设置监视器的根节点,不传则默认为视口。
  2. rootMargin: 类似于 CSS 的 margin 属性。用来缩小或扩大 rootBounds,从而影响相交的触发。
  3. threshold:属性决定相交比例为多少时,触发回调函数。取值为 0 ~ 1,或者 0 ~ 1的数组。 如下图,当我们把 threshold 设置为 [0, 0.25, 0.5, 0.75, 1],绿色方块分别在 0%,25%,50%,75%,100% 可见时,触发回调函数。

Intersection Observer 使用场景

1. 构建自定义的预加载或延迟加载DOM和数据。

比如说我们可以用 Intersection Observer 实现图片的懒加载,当图片达到用户可视区的时候再进行加载。 优化大型网站性能,优先渲染用户可视的视图,根据用户页面滚动,按需加载视图。

2. 实现高性能的滚动列表。

实现列表的上拉加载,减少长列表的滑动卡顿。

3. 计算元素的可视性。

优化广告推广的有效性,只有广告进入用户可视区,才算是有效的广告计数。 实现用户把播放器移除可视区时的自动暂停,进入可视区再进行播放。 还可以高效的实现视差动画。

Intersection Observer 兼容性

IntersectionObserver 目前已经被主流的浏览器支持,我们可以使用渐进支持的方式使用它。对于目前浏览器的生态,我们也要做好向下降级的措施。 我们也可以使用 IntersectionObserver polyfill 增加浏览器的兼容,具体可以查看 polyfill.io。

参考

  1. IntersectionObserver’s Coming into View
  2. w3c Intersection Observer doc