# IntersectionObserver API

时间:2023.09.07

当网页动效较多,常常需要对某些元素是否进入了视口(viewport)进行判断。往往我们采用的方式是通过监听scroll事件、通过类似这样的代码进行判断。

const isElementInViewport = (element) => {
  const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
  const scrollTop = document.documentElement.scrollTop
  return element.offsetTop - scrollTop <= viewPortHeight
}

或者

const isElementInViewport = (el) => {
  const rect = el.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document. documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document. documentElement.clientWidth)
  );
}

查阅文档还有一种方法:IntersectionObserver API

IntersectionObserver API 提供了一种异步检测目标元素与祖先元素或相交情况变化的方法。在目标元素与视口或者其他指定元素发生交集时和触发配置的回调函数。

# 用法

const io = new IntersectionObserver(callback, option);

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

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

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

# callback参数

// 每个成员都是一个IntersectionObserverEntry对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。

callback: (entries) => {}

// entries返回值
{
  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
}

  1. time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒

  2. target:被观察的目标元素,是一个 DOM 节点对象

  3. rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null

  4. boundingClientRect:目标元素的矩形区域的信息

  5. intersectionRect:目标元素与视口(或根元素)的交叉区域的信息

  6. intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0

  7. isIntersecting: 如果返回 true, 则描述了变换到交叉时的状态; 如果返回 false, 变换是从交叉状态到非交叉状态。

# option

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

用户可以自定义这个数组。比如,[0, 0.25, 0.5, 0.75, 1] 就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。

TIP

但是要注意的一点是:IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。

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

参考:

阮一峰 Intersection Observer API (opens new window)

MDN Intersection Observer API (opens new window)