早就想好好总结一下 Intersection Observer 这个 API,苦于一直没有时间,今天就来好好总结一下这个 API。
本篇文章将介绍现代 WebAPI Intersection Observer(元素交集观察器)。
背景
交叉观察器 API(Intersection Observer API)提供了一种异步检测目标元素与祖先元素或顶级文档的视口相交情况变化的方法。
过去,要检测一个元素是否可见或者两个元素是否相交并不容易,很多解决办法不可靠或性能很差。然而,随着互联网的发展,这种需求却与日俱增,比如,下面这些情况都需要用到相交检测:
- 在页面滚动时“懒加载”图像或其他内容。
- 实现“无限滚动”网站,在滚动过程中加载和显示越来越多的内容,这样用户就不必翻页了。
- 报告广告的可见度,以便计算广告收入。
- 根据用户是否能看到结果来决定是否执行任务或动画进程。
交叉观察器 API 可令代码注册一个回调函数,当特定元素进入或退出与另一元素(或视口)的交集时,或者当两个元素之间的交集发生指定变化时,该函数就会被执行。这样,网站就不再需要在主线程上做任何事情来监视这种元素交集,浏览器也可以根据自己的需要优化交集管理。
由于该方法是异步的,所以不会有Element.getBoundingClientRect()
等方法的性能问题。
基本使用
初始化对象IntersectionObserver
对象。
可以看到该配置函数有两个配置项:callback
与options
。我们分别来探讨这两个选项有什么作用:
options
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 意味着在每个像素都可见之前,阈值不会被认为已通过。
callback
callback
作为交集的回调函数,执行特定的行为。
请留意,你注册的回调函数将会在主线程中被执行。所以该函数执行速度要尽可能的快。如果需要执行任何耗时的操作,请使用 Window.requestIdleCallback()
。
配置项详解
root(根元素)
在跟踪元素与容器的交集之前,我们需要知道容器是什么。这个容器就是交集根,或根元素。它可以是文档中作为要观察元素的祖先的特定元素,也可以是 null
,即使用文档的视口作为容器。
注意一般视口用的较多,由于大多数子元素一开始就相交,所以很少使用。判断与父元素相交。
threshold(阈值)
交叉观察器 API
使用阈值,而不是报告目标元素可见度的每一个微小变化。创建观察器时,可以提供一个或多个数值,代表目标元素可见度的百分比。然后,API
只报告超过这些阈值的可见性变化。
例如,如果希望每次目标元素的可见度向后或向前越过每个 25%
的标记时都能得到通知,可以在创建观察器时指定数组 [0, 0.25, 0.5, 0.75, 1]
作为阈值列表。
rootMargin
给根元素添加margin
边距实现收缩的效果
正值矩形向外扩张,负值矩形向内收缩。
结合下面这个例子进行理解
callback
- 页面渲染后会执行一次,无论是否相交。
- 每次进入和离开各执行一次。
API
实例属性
同上options
选项。
实例方法
disconnect
:通知所有对象的监视。
observe
:观察一个对象。
unobserve
:停止观察。
takeRecords
takeRecords 方法的用途是在停止观察之前获取所有未处理的交叉记录,以便在回调函数中处理它们。这个方法返回一个 IntersectionObserverEntry 对象数组,每个对象包含目标元素与根每次的相交信息。
如果你使用回调来监视这些更改,则无需调用此方法。调用此方法会清除挂起的相交状态列表,因此不会运行回调。
IntersectionObserverEntry
属性:
boundingClientRect
:返回目标元素的平面矩形。
intersectionRatio
:相交多少。
intersectionRect
:相交的部分的平面矩形。
isIntersecting
:是否相交。
rootBounds
:使用rootMargin
计算后的平面矩阵。
target
:目标元素。
time
:交集状态发生改变的时候的时间戳。
基本用例
图片懒加载
工具函数
指令封装
无限滚动
底层元素进入视口就开始加载
无限滚动的另一种方式就是触底加载
scrollHeight
是一个元素内容高度的度量,包含由于溢出导致的视图中不可见内容。也就是说,如果一个元素的内容超出了它的可视区域,那么它的 scrollHeight
就会大于它的可视区域的高度。scrollHeight
的值等于元素的最小高度,使得所有的内容都能在不使用垂直滚动条的情况下显示在视图中。scrollHeight
的计算方式和 clientHeight
相同:它包含了元素的内边距,但不包括边框、外边距或水平滚动条(如果有的话)。它也可以包括伪元素(如 ::before
或 ::after
)的高度。
clientHeight
是元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距。也就是说,它是元素的可视区域的高度。如果元素的内容没有超出可视区域,那么它的 clientHeight
就等于它的 scrollHeight
。
scrollTop
是获取或设置一个元素的内容垂直滚动的像素数。也就是说,它是元素的内容顶部和元素的可视区域顶部之间的距离。当元素的内容没有滚动时,它的 scrollTop
为 0。当元素的内容向下滚动时,它的 scrollTop
会增加,直到滚动到内容的底部。
这三个属性的关系可以用下面的公式表示:
- 当元素的内容没有滚动时,
scrollTop = 0
,clientHeight = scrollHeight
。 - 当元素的内容向下滚动时,
scrollTop > 0
,clientHeight < scrollHeight
。 - 当元素的内容滚动到底部时,
scrollTop + clientHeight = scrollHeight
。
这三个属性可以用来实现一些常见的功能,例如:
- 判断一个元素是否可以滚动,可以检查它的
scrollHeight
是否大于它的 clientHeight
。 - 判断一个元素是否已经滚动到底部,可以检查它的
scrollTop + clientHeight
是否等于或接近它的 scrollHeight
。 - 判断一个用户是否已经阅读了一个文本,可以检查一个文本框的
scrollTop + clientHeight
是否等于它的 scrollHeight
。