IntersectionObserver 会通知您,观察到的元素何时进入或退出浏览器视口。
浏览器支持
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
假设您想要跟踪 DOM 中的某个元素何时进入可见视口。您可能需要这样做,以便及时延迟加载图片,或者因为您需要知道用户是否真的在查看某个广告横幅。为此,您可以连接滚动事件,或者使用周期性计时器并调用 getBoundingClientRect()
。
不过,这种方法非常慢,因为每次调用 getBoundingClientRect()
都会迫使浏览器重新布局整个页面,并给您的网站带来明显的卡顿。如果您知道自己的网站在 iframe 中加载,并且想知道用户何时可以看到某个元素,那么问题就几乎是不可能的。单一源模型和浏览器不允许您访问包含 iframe 的网页中的任何数据。例如,对于经常使用 iframe 加载的广告来说,这是一个常见问题。
提高此可见性测试的效率是 IntersectionObserver
的设计目标,它已支持所有现代浏览器。IntersectionObserver
可让您了解观察到的元素何时进入或退出浏览器视口。
如何创建 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
!请告诉我们您的想法。