IntersectionObserver 现已进入用户视野

IntersectionObserver 会通知您,观察到的元素何时进入或退出浏览器视口。

浏览器支持

  • 51
  • 15
  • 55
  • 12.1

来源

假设您想要跟踪 DOM 中的某个元素何时进入可见视口。您可能需要这样做,以便及时延迟加载图片,或者因为您需要知道用户是否真的在查看某个广告横幅。为此,您可以连接滚动事件,或者使用周期性计时器并调用 getBoundingClientRect()

不过,这种方法非常慢,因为每次调用 getBoundingClientRect() 都会迫使浏览器重新布局整个页面,并给您的网站带来明显的卡顿。如果您知道自己的网站在 iframe 中加载,并且想知道用户何时可以看到某个元素,那么问题就几乎是不可能的。单一源模型和浏览器不允许您访问包含 iframe 的网页中的任何数据。例如,对于经常使用 iframe 加载的广告来说,这是一个常见问题。

提高此可见性测试的效率是 IntersectionObserver 的设计目标,它已支持所有现代浏览器。IntersectionObserver 可让您了解观察到的元素何时进入或退出浏览器视口。

iframe 显示设置

如何创建 IntersectionObserver

该 API 相当小,最好使用以下示例进行描述:

const io = new IntersectionObserver(entries => {
  console.log(entries);
}, {
  /* Using default options. Details below */
});

// Start observing an element
io.observe(element);

// Stop observing an element
// io.unobserve(element);

// Disable entire IntersectionObserver
// io.disconnect();

使用 IntersectionObserver 的默认选项时,系统会在元素部分进入视图及完全离开视口时调用回调。

如果您需要观察多个元素,建议通过多次调用 observe(),使用同一 IntersectionObserver 实例观察多个元素。

系统会将 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: div#observee
    // ...

rootBounds 是对根元素(默认为视口)调用 getBoundingClientRect() 的结果。boundingClientRect 是对所观察的元素调用 getBoundingClientRect() 的结果。intersectionRect 是这两个矩形的交集,可有效告知您所观察元素的哪一部分可见。intersectionRatio 密切相关,可让您了解元素可见的大小。掌握这些信息后,您现在就可以实现各种功能,例如在素材资源显示在屏幕上之前即时加载它们。高效。

交叉率。

IntersectionObserver 会异步传送其数据,而您的回调代码将在主线程中运行。此外,该规范实际上指出,IntersectionObserver 实现应使用 requestIdleCallback()。这意味着,对您提供的回调的调用优先级较低,并且将由浏览器在空闲时间内进行。这是一个有意识的设计决策。

滚动 div

我不太喜欢在元素内滚动,但我不是来评判的,IntersectionObserver 也一样。options 对象采用 root 选项,您可以通过该选项将视口的替代选项定义为根。请务必注意,root 必须是所有观察到元素的祖先实体。

让所有东西都交错!

否!开发者糟糕!这并没有注意用户对 CPU 周期的使用情况。让我们以一个无限滚动条为例:在这种情况下,强烈建议您向 DOM 添加“哨兵”并观察(并回收!)这些元素。您应在无限滚动条中的最后一项附近添加一个标记。当该标记出现时,您可以使用回调加载数据,创建后续项,将它们附加到 DOM,并相应地调整标记的位置。如果您正确回收标记,则无需额外调用 observe()IntersectionObserver会继续工作。

无限滚动

请提供更多最新动态

如前所述,当观察到的元素部分进入视图时,将触发一次回调,另一次会在其离开视口时触发回调。这样,IntersectionObserver 就可以为您解答“元素 X 是否在视图中?”这一问题。但在某些用例中,这可能还不够。

这就是 threshold 选项的用武之地。您可以定义一组 intersectionRatio 阈值。每当 intersectionRatio 与其中一个值交集时,系统就会调用您的回调。threshold 的默认值为 [0],解释了此默认行为。如果我们将 threshold 更改为 [0, 0.25, 0.5, 0.75, 1],则每当有四分之一的元素可见时,我们都会收到通知:

阈值动画。

还有其他方案吗?

到目前为止,除了上面列出的选项之外,只有一个其他选项。借助 rootMargin,您可以指定根部的外边距,从而有效地扩大或缩小相交点所使用的区域。这些外边距使用 CSS 样式的字符串 á la "10px 20px 30px 40px" 指定,分别指定上边距、右边距、左边距和左边距。总而言之,IntersectionObserver 选项结构体提供以下选项:

new IntersectionObserver(entries => {/* … */}, {
  // The root to use for intersection.
  // If not provided, use the top-level document's viewport.
  root: null,
  // Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.
  // If an explicit root element is specified, components may be percentages of the
  // root element size.  If no explicit root element is specified, using a
  // percentage is an error.
  rootMargin: "0px",
  // Threshold(s) at which to trigger callback, specified as a ratio, or list of
  // ratios, of (visible area / total area) of the observed element (hence all
  // entries must be in the range [0, 1]).  Callback will be invoked when the
  // visible ratio of the observed element crosses a threshold in the list.
  threshold: [0],
});

<iframe>魔法

IntersectionObserver 在设计时就考虑到了广告服务和社交网络微件,它们经常使用 <iframe> 元素,如果知道它们是否位于视图中,好处多多。如果 <iframe> 观察到它的某个元素,则滚动 <iframe> 和滚动包含 <iframe> 的窗口都会在适当的时间触发回调。不过,对于后一种情况,rootBounds 将设置为 null,以避免跨源泄露数据。

IntersectionObserver 涉及的是什么?

需要注意的是,IntersectionObserver 并非有意为之,即既无法实现像素完美,也无法实现低延迟。使用它们实现诸如依赖于滚动的动画等操作必定会失败,因为严格来说,使用数据时,数据会过时。说明文档详细介绍了 IntersectionObserver 的原始用例。

我可以在回调中完成多少工作?

简明扼要:在回调上花费太多时间会导致应用延迟 - 所有常见做法都适用。

向前,让这些元素交汇在一起

浏览器对 IntersectionObserver 的支持很好,因为它适用于所有现代浏览器。如有必要,您可以在旧版浏览器中使用 polyfill,还可以在 WICG 的代码库中找到该 polyfill。显然,使用 polyfill 无法获得原生实现能够带来的性能优势。

您可以立即开始使用 IntersectionObserver!请告诉我们您的想法。