前言
前端开发肯定离不开判断一个元素是否能被用户看见,然后再基于此进行一些交互。
过去,要检测一个元素是否可见或者两个元素是否相交并不容易,很多解决办法不可靠或性能很差。然而,随着互联网的发展,这种需求却与日俱增,比如,下面这些情况都需要用到相交检测:
- 图片懒加载——当图片滚动到可见时才进行加载
- 内容无限滚动——也就是用户滚动到接近内容底部时直接加载更多,而无需用户操作翻页,给用户一种网页可以无限滚动的错觉
- 检测广告的曝光情况——为了计算广告收益,需要知道广告元素的曝光情况
- 在用户看见某个区域时执行任务或播放动画
过去,相交检测通常要用到事件监听,并且需要频繁调用
Element.getBoundingClientRect()
方法以获取相关元素的边界信息。事件监听和调用Element.getBoundingClientRect()
都是在主线程上运行,因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅。
上面这一段话来自 MDN
,中心思想就是现在判断一个元素是否能被用户看见的使用场景越来越多,监听 scroll
事件以及通过 Element.getBoundingClientRect()
获取节点位置的方式,又麻烦又不好用,那么怎么办呢。于是就有了今天的内容 Intersection Observer API。
Intersection Observer API 是什么
我们需要观察的元素被称为 目标元素(target),设备视窗或者其他指定的元素视口的边界框我们称它为 根元素(root),或者简称为 根 。
Intersection Observer API
翻译过来叫做 “交叉观察器”,因为判断元素是否可见(通常情况下)的本质就是判断目标元素和根元素是不是产生了 交叉区域 。
为什么是通常情况下,因为当我们 css
设置了 opacity: 0
,visibility: hidden
或者 用其他的元素覆盖目标元素
的时候,对于视图来说是不可见的,但对于交叉观察器来说是可见的。这里可能有点抽象,大家只需记住,交叉观察器只关心 目标元素 和 根元素 是否有 交叉区域, 而不管视觉上能不能看见这个元素。当然如果设置了 display:none
,那么交叉观察器就不会生效了,其实也很好理解,因为元素已经不存在了,那么也就监测不到了。
一句话总结:Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。 -- MDN
现在不懂没关系,咱们接着往下看,看完自然就明白了。
Intersection Observer API 怎么玩
生成观察器
// 调用构造函数 IntersectionObserver 生成观察器 const myObserver = new IntersectionObserver(callback, options);
首先调用浏览器原生构造函数 IntersectionObserver
,构造函数的返回值是一个 观察器实例 。
构造函数 IntersectionObserver
接收两个参数
- callback: 可见性发生变化时触发的回调函数
- options: 配置对象(可选,不传时会使用默认配置)
构造函数接收的参数 options
为了方便理解,我们先看第二个参数 options
。一个可以用来配置观察器实例的对象,那么这个配置对象都包含哪些属性呢?
-
root: 设置目标元素的根元素,也就是我们用来判断元素是否可见的区域,必须是目标元素的父级元素,如果不指定的话,则使用浏览器视窗,也就是
document
。 -
rootMargin: 一个在计算交叉值时添加至根的边界中的一组偏移量,类型为字符串
(string)
,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和CSS 中margin
属性等同,默认值“0px 0px 0px 0px”
,如果有指定root
参数,则rootMargin
也可以使用百分比来取值。 -
threshold: 介于
0
和1
之间的数字,指示触发前应可见的百分比。也可以是一个数字数组,以创建多个触发点,也被称之为 阈值。如果构造器未传入值, 则默认值为0
。 -
trackVisibility: 一个布尔值,指示当前观察器是否将跟踪目标可见性的更改,默认为
false
,注意,此处的可见性并非指目标元素和根元素是否相交,而是指视图上是否可见,这个我们之前就已经分析过了,如果此值设置为false
或不设置,那么回调函数参数中IntersectionObserverEntry
的isVisible
属性将永远返回false
。 -
delay: 一个数字,也就是回调函数执行的延迟时间(毫秒)。如果
trackVisibility
设置为true
,则此值必须至少设置为100
,否则会报错(但是这里我也只是亲测出来的,并不知道为什么会设计成这样,如果有大佬了解请指教一下)。
构造函数接收的参数 callback
当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数:存放 IntersectionObserverEntry
对象的数组和观察器实例(可选)。
((doc) => { //回调函数 const callback = (entries, observer) => { console.log('