交叉观察器 API(Intersection Observer API)提供了一种异步检测目标元素与祖先元素或顶级文档的视口相交情况变化的方法。
过去,要检测一个元素是否可见或者两个元素是否相交并不容易,很多解决办法不可靠或性能很差。然而,随着互联网的发展,这种需求却与日俱增,比如,下面这些情况都需要用到相交检测:
1.在页面滚动时“懒加载”图像或其他内容。2.实现“无限滚动”网站,在滚动过程中加载和显示越来越多的内容,这样用户就不必翻页了。3.报告广告的可见度,以便计算广告收入。4.根据用户是否能看到结果来决定是否执行任务或动画进程。
过去实施相交检测时,需要调用事件处理程序和循环方法,如 Element.getBoundingClientRect() 来为每个受影响的元素建立所需的信息。由于所有这些代码都在主线程上运行,因此即使是其中的一行代码也会导致性能问题。当网站加载这些测试时,情况会变得非常糟糕。
考虑一个使用无限滚动的网页。它使用一个供应商提供的库来管理整个页面中周期性放置的广告,页面到处都是动画图形,并使用一个自定义库来绘制通知框和其他相似的东西。每个库都有自己的相交检测例程,全部在主线程上运行。网站的作者可能根本没有意识到这一点,因为他们可能对所使用的两个库的内部运作知之甚少。当用户滚动页面时,这些相交检测例程会在滚动处理代码中不断触发,导致用户对浏览器、网站和他们的电脑感到失望。
交叉观察器 API 可令代码注册一个回调函数,当特定元素进入或退出与另一元素(或视口)的交集时,或者当两个元素之间的交集发生指定变化时,该函数就会被执行。这样,网站就不再需要在主线程上做任何事情来监视这种元素交集,浏览器也可以根据自己的需要优化交集管理。
交叉观察器 API 无法提供重叠像素的确切数量或具体是哪些像素重叠;不过,它涵盖了“如果它们相交N%左右,我需要做什么”这种更常见的用例。
交叉观察器的概念和用法
交叉观察器 API 允许你配置一个回调函数,当以下情况发生时会被调用:
1.目标元素与设备视口或指定元素相交。在交叉观察器 API 中,指定元素被称为根元素或根。2.观察器(Observer)第一次监听观察目标元素。
通常情况下,需要观察目标元素最近的可滚动祖先的交集变化,如果目标元素不是可滚动元素的后代,则需要观察设备视口的交集变化。要观察相对于设备视口的交集,请为 root 选项指定 null。请继续阅读有关交叉观察器选项的更详细说明。
无论你是使用视口还是其他元素作为根元素,API 的工作方式都是一样的,只要目标元素的可见性发生变化,与根元素的交集达到所需的程度,就会执行你提供的回调函数。
目标元素与其根元素的交集程度就是交叉比。它表示目标元素可见的百分比,数值介于 0.0 和 1.0 之间。
创建一个交叉观察器
通过调用 IntersectionObserver 构造函数,创建交叉观测器,并将回调函数传给它,当一个方向或另一个方向越过阈值时,就运行该函数:
let options = {
root: document.querySelector("#scrollArea"),
rootMargin: "0px",
threshold: 1.0,
};
let observer = new IntersectionObserver(callback, options);
阈值为 1.0 意味着目标元素完全出现在 root 选项指定的元素中 100% 可见时,回调函数将会被执行。
传递到 IntersectionObserver() 构造函数的 options 对象,可以控制在什么情况下调用观察器的回调。它有以下字段:
root
用作视口的元素,用于检查目标的可见性。必须是目标的祖先。如果未指定或为 null,则默认为浏览器视口。
rootMargin
根周围的边距。其值可以类似于 CSS margin 属性,例如 "10px 20px 30px 40px"(上、右、下、左)。这些值可以是百分比。在计算交叉点之前,这组值用于增大或缩小根元素边框的每一侧。默认值为全零。
threshold
一个数字或一个数字数组,表示目标可见度达到多少百分比时,观察器的回调就应该执行。如果只想在能见度超过 50% 时检测,可以使用 0.5 的值。如果希望每次能见度超过 25% 时都执行回调,则需要指定数组 [0, 0.25, 0.5, 0.75, 1]。默认值为 0(这意味着只要有一个像素可见,回调就会运行)。值为 1.0 意味着在每个像素都可见之前,阈值不会被认为已通过。
定位要观察的元素
创建一个观察器后,需要给定一个目标元素进行观察。
let target = document.querySelector("#listItem");
observer.observe(target);
// 我们为观察器设置的回调将在第一次执行,
// 它将等待我们为观察器分配目标(即使目标当前不可见)
每当目标满足该 IntersectionObserver 指定的阈值(threshold),回调被调用。回调接收 IntersectionObserverEntry 对象和观察器的列表:
let callback = (entries, observer) => {
entries.forEach((entry) => {
// 每个条目描述一个目标元素观测点的交叉变化:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
回调接收到的条目列表包括每个报告了相交状态变化的目标的一个条目。检查 isIntersecting 属性的值,查看条目是否代表当前与根相交的元素。
请留意,你注册的回调函数将会在主线程中被执行。所以该函数执行速度要尽可能的快。如果需要执行任何耗时的操作,请使用 Window.requestIdleCallback()。
此外,请注意,如果指定了 root 选项,目标必须是根元素的后代。
用法总结
Intersection Observer 是一个用于监测目标元素与其祖先元素或视口之间交叉状态的 API。它提供了一种有效的方式来观察元素是否进入或离开另一个元素或视口。
使用 Intersection Observer,你可以监听目标元素与视口或容器之间的交叉状态,例如元素进入视口、元素完全可见、元素离开视口等。这对于实现无限滚动、懒加载、可视化数据统计等功能非常有用。
下面是 Intersection Observer 的基本用法:
1、创建 Intersection Observer 对象:
const observer = new IntersectionObserver(callback, options);
•callback
是一个回调函数,用于处理目标元素与视口或容器之间交叉状态的变化。当交叉状态发生变化时,该回调函数会被触发。•options
是一个配置对象,用于指定观察器的选项,例如观察器的根元素、阈值等。
2、将目标元素添加到观察器中:
observer.observe(targetElement);
•targetElement
是要观察的目标元素。
3、定义回调函数:
const callback = (entries, observer) => {
entries.forEach(entry => {
// 处理交叉状态变化
if (entry.isIntersecting) {
// 目标元素进入视口
} else {
// 目标元素离开视口
}
});
};
•entries
是一个 IntersectionObserverEntry 对象的数组,每个对象表示一个目标元素与根元素或视口的交叉状态信息。•isIntersecting
属性表示目标元素当前是否与根元素或视口交叉。
4、停止观察:
observer.unobserve(targetElement);
•targetElement
是要停止观察的目标元素。
通过上述步骤,你可以创建 Intersection Observer 对象并开始观察目标元素。当目标元素与根元素或视口的交叉状态发生变化时,触发回调函数,并根据交叉状态进行相应的处理。
除了基本用法外,Intersection Observer 还提供了其他选项,例如设置阈值、观察容器元素等。这些选项可以根据你的需求进行配置,以实现更精确的交叉状态监测。