按照惯例,我们先来看看 IntersectionObserver
到底是个什么东西,MDN 上的介绍的是,IntersectionObserver
接口(从属于 Intersection Observer API),提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport
)交叉状态的方法,祖先元素与视窗(viewport
)被称为根(root
)
当一个 IntersectionObserver
对象被创建时,其被配置为监听根中一段给定比例的可见区域,一旦 IntersectionObserver
被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值,然而你可以在同一个观察者对象中配置监听多个目标元素
看上去云里雾里的,其实简单的总结一下,所谓的 IntersectionObserver
说的就是一种监听目标元素与其祖先或视窗交叉状态的手段,也就是观察一个元素是否在视窗可见
如上图,交叉了就是说明当前元素在视窗里,当前就是可见的了,但是有一点特别需要注意的就是,必须是子元素跟父(祖先)元素发生交叉,如果你想检查两个非父子关系的交叉,是不可以的
本文所有示例可见 IntersectionObserver
基本概念
先来看看如何使用,使用方式很简单
1 | const io = new IntersectionObserver(callback, options) |
下面我们就来详细介绍具体的配置参数
构造函数
1 | new IntersectionObserver(callback, options) |
其实就是一个简单的构造函数,它会返回一个 IntersectionObserver
实例,接收两个参数
callback
,是当元素的可见性变化时候的回调函数options
,是一些配置项(可选的)
callback
当元素的可见性变化时,就会触发 callback
函数,callback
函数会触发两次,元素进入视窗(开始可见时)和元素离开视窗(开始不可见时)都会触发,它接受一个 entries
参数,返回当前已监听并且发生了交叉的目标集合
1 | var io = new IntersectionObserver((entries) => { |
运行结果如下($0
表示上一次审查元素的选择的节点)
我们可以看到 callback
函数有个 entries
参数,它是个 IntersectionObserverEntry
对象数组,各个属性如下
属性 | 解释 |
---|---|
boundingClientRect |
目标元素的矩形信息 |
intersectionRatio |
相交区域和目标元素的比例值 intersectionRect/boundingClientRect ,不可见时小于等于 0 |
intersectionRect |
目标元素和视窗(根)相交的矩形信息,可以称为相交区域 |
isIntersecting |
目标元素当前是否可见值,如果可见则为 true |
rootBounds |
根元素的矩形信息,没有指定根元素就是当前视窗的矩形信息 |
target |
观察的目标元素 |
time |
返回一个记录从 IntersectionObserver 的时间到交叉被触发的时间的时间戳 |
如下图所示
options
几个比较常用的参数如下
root
用于观察的根元素,默认是浏览器的视口,也可以指定具体元素,指定元素的时候用于观察的元素必须是指定元素的子元素
threshold
用来指定交叉比例,决定什么时候触发回调函数,是一个数组,默认是 [0]
1 | const options = { |
上面代码,我们指定了交叉比例为 [0, 0.5, 1]
,当观察元素 img
处于 0%
、50%
、100%
时候就会触发回调函数
rootMargin
用来扩大或者缩小视窗的的大小,使用 CSS
的定义方法,10px 10px 30px 20px
表示 top
、right
、bottom
和 left
的值
1 | const options = { |
为了方便理解,可以参考下图
首先我们先来看看上图当中的蓝线部分,它就是我们定义的 root
元素,我们添加了 rootMargin
属性,将视窗的增大了,虚线就是现在的视窗,所以元素现在也就在视窗里面了,简单来说就是自定义元素进入视窗的距离,由此可见,root
元素只有在 rootMargin
为空的时候才是绝对的视窗
实际应用
在了解完 IntersectionObserver
的相关概念之后,我们来看几个具体的实例,本文所有示例可见 IntersectionObserver
监听元素
先来看一个最简单的示例,主要代码如下
1 | let box = document.querySelector('.box') |
结果如下图所示
打开控制台可以发现,每次当元素进入或者离开可视区的时候,控制台当中就会输出对应的字段,我们再来稍微的调整一下,将一个元素替换为多个元素,代码如下
1 | let box02 = document.querySelectorAll('.box') |
结果如下图所示
根据上图可以发现,虽然元素变成了三个,但是每次发生交叉的时候是一起发生的,再来调整一下,同样还是三个元素,但是排列方式不一样,结果如下图所示
可以发现,在这种情况之下每个目标轮流发生交叉,且当前只触发了一个,所以每次返回的集合长度只有一,如果想指定监听父元素,可以使用 root
参数
1 | let box = document.querySelector('.box') |
现在我们已经基本了解了 IntersectionObserver
的一些基本用法,下面我们就来看一些实际场景当中的应用
图片懒加载
以前的做法是监听浏览器滚动,然后遍历拿到每个图片的空间信息,然后判断一些位置信息从而进行图片加载,而现在只需要交给交叉观察者去做,页面布局如下
1 | <!-- 多个 img 元素 --> |
然后针对图片列表进行监听
1 | let images = document.querySelectorAll('img') |
效果如下,只有当对应图片进入可视区以后才会进行加载
也可以通过设置 rootMargin
,来自定义图片进入视窗多少距离以后在进行加载,另外,使用该方法如果是横向滚动懒加载一样是可以使用的
传统的懒加载只是监听全局滚动条的滚动,因为它并不是判断目标是否出现在视窗,所以横向的图片会一起加载,即使你没有向左滑动,所以这也是交叉观察者的一大优点
触底
使用交叉观察者可以让我们轻松实现滚动到底部自定加载的功能,我们首先在列表底部放一个参照元素,然后使用交叉观察者去监听,布局如下
1 | <div class="box"> |
class
为 footer
的元素就作为我们的参照元素,下面就可以对其进行监听
1 | new IntersectionObserver(entries => { |
结果如下
动画展示
一个比较常见的效果,即当某个元素或者列表出现的时候就给该元素或者列表加个动画,比如渐变、偏移等,下面我们就来实现看看,首先是页面布局,跟上面的示例其实差不多,但是这里采用两列,主要 CSS
如下
1 | li.show { |
最终效果如下
兼容性
IntersectionObserver
的兼容性可见下图
从上图可以发现,IE
是不兼容的,但是官方提供了 polyfill
,详细可见 IntersectionObserver