Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

神奇的交叉观察器 - IntersectionObserver #13

Open
li-jia-nan opened this issue Oct 15, 2022 · 0 comments
Open

神奇的交叉观察器 - IntersectionObserver #13

li-jia-nan opened this issue Oct 15, 2022 · 0 comments

Comments

@li-jia-nan
Copy link
Owner

1. 背景

网页开发时,不管是在移动端,还是PC端,都有个很重要的概念,叫做动态懒加载,适用于一些图片资源(或者数据)特别多的场景中,这个时候,我们常常需要了解某个元素是否进入了“视口”(viewport),即用户能不能看到它。

传统的实现方法是,监听到scroll事件或者使用setInterval来判断,调用目标元素的getBoundingClientRect()方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。这种方法的缺点是,由于scroll事件触发频率高,计算量很大,如果不做防抖节流的话,很容易造成性能问题,而setInterval由于其有间歇期,也会出现体验问题。

所以在几年前,Chrome率先提供了一个新的API,就是IntersectionObserver,它可以用来自动监听元素是否进入了设备的可视区域之内,而不需要频繁的计算来做这个判断。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。

2. 兼容性

由于这个api问世已经很多年了,所以对浏览器的支持性还是不错的,完全可以上生产环境,点击这里可以看看当前浏览器对于IntersectionObserver的支持性:

111111.png

3. 用法

API的调用非常简单:

const io = new IntersectionObserver(callback, option);

上面代码中,IntersectionObserver 是浏览器原生提供的构造函数,接受两个参数:

  • callback:可见性发现变化时的回调函数
  • option:配置对象(可选)。

构造函数的返回值是一个观察器实例。实例一共有4个方法:

  • observe:开始监听特定元素
  • unobserve:停止监听特定元素
  • disconnect:关闭监听工作
  • takeRecords:返回所有观察目标的对象数组

3.1 observe

该方法需要接收一个target参数,值是Element类型,用来指定被监听的目标元素

// 获取元素
const target = document.getElementById("dom");

// 开始观察
io.observe(target);

3.2 unobserve

该方法需要接收一个target参数,值是Element类型,用来指定停止监听的目标元素

// 获取元素
const target = document.getElementById("dom");

// 停止观察
io.unobserve(target);

3.3 disconnect

该方法不需要接收参数,用来关闭观察器

// 关闭观察器
io.disconnect();

3.4 takeRecords

该方法不需要接收参数,返回所有被观察的对象,返回值是一个数组

// 获取被观察元素
const observerList = io.takeRecords();

注意:

observe方法的参数是一个 DOM 节点,如果需要观察多个节点,就要多次调用这个方法:

// 开始观察多个元素
io.observe(domA);
io.observe(domB);
io.observe(domC);

4. callback 参数

目标元素的可见性变化时,就会调用观察器的回调函数callback

callback一般会触发两次。一次是目标元素刚刚进入视口,另一次是完全离开视口。

const io = new IntersectionObserver((changes, observer) => {
  console.log(changes);
  console.log(observer);
});

上面代码中,callback函数的参数接收两个参数changesobserver

  • changes:这是一个数组,每个成员都是一个被观察对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,那么changes数组里面就会打印出两个元素,如果只观察一个元素,我们打印changes[0]就能获取到被观察对象
  • observer: 这是一个对象,返回我们在实例中传入的第二个参数option(如果没传,则返回默认值)

5. IntersectionObserverEntry 对象

上面提到的changes数组中的每一项都是一个IntersectionObserverEntry 对象(下文简称io对象),对象提供目标元素的信息,一共有八个属性,我们打印这个对象:

// 创建实例
const io = new IntersectionObserver(changes => {
  changes.forEach(change => {
    console.log(change);
  });
});

// 获取元素
const target = document.getElementById("dom");

// 开始监听
io.observe(target);

运行上面代码,并且改变dom的可见性,这时控制台可以看到一个对象:

555.png

每个属性的含义如下:

  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • isIntersecting: 布尔值,目标元素与交集观察者的根节点是否相交
  • isVisible: 布尔值,目标元素是否可见(该属性还在试验阶段,不建议在生产环境中使用)
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • target:被观察的目标元素,是一个 DOM 节点对象
  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒

6. 应用

  1. 预加载(滚动加载,翻页加载,无限加载)

  2. 懒加载(后加载、惰性加载)

  3. 其它

7. 注意点

IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。

规格写明,IntersectionObserver的实现,应该采用requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。

8. 参考链接

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant