Api IntersectionObserver

针对滚动监听的一个小有意思的api

IntersectionObserver

IntersectionObserver API 是用来监视某个元素是否滚动进了浏览器窗口的可视区域(视口)或者滚动进了它的某个祖先元素的可视区域内。它的主要功能是用来实现延迟加载和展现量统计。

![img](02 IntersectionObserver.assets/116671-20160605131111086-768036414.png)

它提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)

构造函数

1
new IntersectionObserver(callback, options)

callback 是个必选参数,当有相交发生时,浏览器便会调用它,后面会详细介绍;

options 整个参数对象以及它的三个属性都是可选的:

root

根元素,它有很多后代元素,想要做的就是判断它的某个后代元素是否滚动进了自己的可视区域范围。这个 root 参数就是用来指定根元素的,默认值是 null。即浏览器窗口。

需要注意的一点是,如果 root 不是 null,那么相交区域就不一定在视口内了,因为 root 和 target 的相交也可能发生在视口下方

threshold

当目标元素和根元素相交时,用相交的面积除以目标元素的面积会得到一个 0 到 1(0% 到 100%)的数值

![img](02 IntersectionObserver.assets/116671-20160608175232261-11872166.png)

IntersectionObserver API 的基本工作原理就是:当目标元素和根元素相交的面积占目标元素面积的百分比到达或跨过某些指定的临界值时就会触发回调函数threshold 参数就是用来指定那个临界值的,默认值是 0,表示俩元素刚刚挨上就触发回调。有效的临界值可以是在 0 到 1 闭区间内的任意数值,比如 0.5 表示当相交面积占目标元素面积的一半时触发回调。而且可以指定多个临界值,用数组形式,比如 [0, 0.5, 1],表示在两个矩形开始相交,相交一半,完全相交这三个时刻都要触发一次回调函数。如果你传了个空数组,它会给你自动插入 0,变成 [0],也等效于默认值 0。

rootMagin

可以给根元素添加一个假想的 margin,从而对真实的根元素区域进行缩放。比如当 root 为 null 时设置 rootMargin: “100px”,实际的根元素矩形四条边都会被放大 100px

![img](02 IntersectionObserver.assets/116671-20160606131900605-2121804167.png)

实例方法

observe()

观察某个目标元素,一个观察者实例可以观察任意多个目标元素。注意,这不是事件,没有冒泡。

unobserve()

取消对某个目标元素的观察,延迟加载通常都是一次性的,observe 的回调里应该直接调用 unobserve() 那个元素.

disconnect()

取消观察所有已观察的目标元素

takeRecords()

使用案例很少,不过多说明

理解这个方法需要讲点底层的东西:在浏览器内部,当一个观察者实例在某一时刻观察到了若干个相交动作时,它不会立即执行回调,它会调用 window.requestIdleCallback() (目前只有 Chrome 支持)来异步的执行我们指定的回调函数,而且还规定了最大的延迟时间是 100 毫秒,相当于浏览器会执行

回调函数

回调函数共有两个参数,第二个参数就是观察者实例本身,一般没用,因为实例通常我们已经赋值给一个变量了,而且回调函数里的 this 也是那个实例。

第一个参数是个包含有若干个 IntersectionObserverEntry 对象的数组。每个 IntersectionObserverEntry 对象都代表一次相交,它的属性们就包含了那次相交的各种信息。entries 数组中 IntersectionObserverEntry 对象的排列顺序是按照它所属的目标元素当初被 observe() 的顺序排列的。

entries 数组中的实例有如下属性

time

相交发生时距离页面打开时的毫秒数(有小数),也就是相交发生时 performance.now() 的返回值,比如 60000.560000000005,表示是在页面打开后大概 1 分钟发生的相交。在回调函数里用 performance.now() 减去这个值,就能算出回调函数被 requestIdleCallback 延迟了多少毫秒:

1
2
3
4
5
6
7
<script>
let observer = new IntersectionObserver(([entry]) => {
document.body.textContent += `相交发生在 ${performance.now() - entry.time} 毫秒前`
})

observer.observe(document.documentElement)
</script>

target

相交发生时的目标元素,因为一个根元素可以观察多个目标元素,所以这个 target 不一定是哪个元素

rootBounds

一个对象值,表示发生相交时根元素可见区域的矩形信息,像这样

1
2
3
4
5
6
7
8
{
"top": 0,
"bottom": 600,
"left": 0,
"right": 1280,
"width": 1280,
"height": 600
}

boundingClientRect

发生相交时目标元素的矩形信息,等价于 target.getBoundingClientRect()

intersectionRect

根元素和目标元素相交区域的矩形信息。

intersectionRatio

0 到 1 的数值,表示相交区域占目标元素区域的百分比,也就是 intersectionRect 的面积除以 boundingClientRect 的面积得到的值

实用场景

惰性加载(lazy load)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function query(selector) {
return Array.from(document.querySelectorAll(selector));
}

var observer = new IntersectionObserver(
function(changes) {
changes.forEach(function(change) {
var container = change.target;
var content = container.querySelector('template').content;
container.appendChild(content);
observer.unobserve(container);
});
}
);

query('.lazy-loaded').forEach(function (item) {
observer.observe(item);
});

无限滚动(infinite scroll)

1
2
3
4
5
6
7
8
9
10
11
12
var intersectionObserver = new IntersectionObserver(
function (entries) {
// 如果不可见,就返回
if (entries[0].intersectionRatio <= 0) return;
loadItems(10);
console.log('Loaded new items');
});

// 开始观察
intersectionObserver.observe(
document.querySelector('.scrollerFooter')
);
作者

徐云飞

发布于

2023-02-05

更新于

2023-02-05

许可协议

评论