IntersectionObserver

891 阅读3分钟

前言

IntersectionObserver是一个交叉观察器,与视口有着紧密相连的关系,意思就是我们观察一个元素,当该元素与视口有接触(进入、离开)时,则会观察到其变化,这就是 IntersectionObserver

其通常用来做一些提升体验的工作,一般用的不多

视口:我们用户看到的屏幕就可以做视口,例如:我们的手机屏幕就那么大,微信朋友圈内容很多,我们一次只能看到窗口大小的内容,而实际内容比窗口大小要大,通过滑动改变内容位置,让其进入屏幕(视口),从而让用户看到其他内容

IntersectionObserver

IntersectionObserver使用非常简单,创建后,可以通过设置观察 dom 监听指定 dom与视口的交叉情况,观察到有接触(进入、离开)则会回调 callback函数

image.png

//创建 观察对象
//var io = new IntersectionObserver(callback, option);
var io = new IntersectionObserver((entry) => {}, option);

// 开始观察
io.observe(document.getElementById('exampleDom'));

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

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

callback回调

当被观察元素监听到接触时,会回调我们声明的 callback 回调

callback 返回的内容有如下参数,可以根据实际需要使用

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
{
  time: 3893.92,
  rootBounds: ClientRect {
    bottom: 920,
    height: 1024,
    left: 0,
    right: 1024,
    top: 0,
    width: 920
  },
  boundingClientRect: ClientRect {
     // ...
  },
  intersectionRect: ClientRect {
    // ...
  },
  intersectionRatio: 0.54,
  target: element
}

options

第二个参数 options是我们要关注的细节使用了,其有三个参数

interface IntersectionObserverInit {
    root?: Element | Document | null; //监听根节点
    rootMargin?: string; //根节点偏移
    threshold?: number | number[]; //
}

threshold属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(上面回调返回的intersectionRatio)达到0时触发回调函数。

new IntersectionObserver(
   entries = {/* ... */}, 
   {
     threshold: [0, 0.25, 0.5, 0.75, 1]
   });

很多时候,目标元素不仅会随着窗口滚动,还会在容器里面滚动(比如在iframe窗口里滚动),容器内滚动也会影响目标元素的可见性

root属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。

上面代码中,除了root属性,还有rootMargin属性。后者定义根元素的margin,用来扩展或缩小rootBounds这个矩形的大小,从而影响intersectionRect交叉区域的大小。它使用CSS的定义方法,比如10px 20px 30px 40px,表示 top、right、bottom 和 left 四个方向的值。

这样设置以后,不管是窗口滚动或者容器内滚动,只要目标元素可见性变化,都会触发观察器。

异步触发回调

js 是一门异步性很好的开发语言,IntersectionObserver 也不例外,为了避免对系统造成性能问题,IntersectionObserver 也是是异步的,不随着目标元素的滚动同步触发

IntersectionObserver的实现采用requestIdleCallback(),其优先级非常低,只有线程空闲下来(没有任务执行时),才会执行该观察器

有时候在学 promise 与微任务时,会提到 process.nextTick(将任务放到当前执行栈尾部,其他任务之前,nodejs 环境持有,setImmediate类似)、setTimeout(添加任务到宏队列,消息队列分为微队列和宏队列,宏队列优先级比较低)等,requestIdleCallback(浏览器空闲调用)有时也会被提及,毕竟也算是优化时,时不时会用到的,这里也提一下