IntersectionObserver 现已进入用户视野

IntersectionObserver 可让您知道被观察的元素何时进入或离开浏览器的视口。

浏览器支持

  • Chrome:51.
  • Edge:15.
  • Firefox:55.
  • Safari: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],则每当元素的另外 1/4 变得可见时,我们都会收到通知:

阈值动画。

还有其他选项吗?

目前,除了上述选项外,还有一个额外的选项。借助 rootMargin,您可以为根指定边距,从而有效地扩大或缩小用于相交的区域。这些边距使用 CSS 样式的字符串指定,如 "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 专为广告服务和社交网络 widget 而设计,这些 widget 经常使用 <iframe> 元素,并且可以通过了解这些元素是否在视野范围内获益。如果 <iframe> 观察其中一个元素,滚动 <iframe> 以及滚动包含 <iframe> 的窗口都会在适当的时间触发回调。不过,对于后一种情况,rootBounds 将设置为 null,以避免跨源泄露数据。

IntersectionObserver 不适用于哪些情况?

请注意,IntersectionObserver 故意不追求像素级完美,也不追求低延迟。使用它们来实现滚动依赖型动画等任务注定会失败,因为从严格意义上讲,当您需要使用这些数据时,数据已经过时。说明文档详细介绍了 IntersectionObserver 的原始用例。

我可以在回调中执行多少工作?

简洁明了:在回调中花费太多时间会导致应用延迟,请遵循所有常见做法。

继续前进,交叉你的元素

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

您可以立即开始使用 IntersectionObserver!请告诉我们您想出了什么。