IntersectionObserver 可让您了解被观察到的元素何时进入或退出浏览器的视口。
假设您想跟踪 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
options 结构体提供以下选项:
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
了!请告诉我们你的想法。