Skip to content

IntersectionObserver

IntersectionObserver 是一种用于检测元素与其祖先或顶级文档视口之间的交叉状态的 API。 可以在元素进入或离开视口时触发回调函数。

它是一种方便且简化的手段,使得开发者能够更高效地处理懒加载、无限滚动、曝光统计等场景,而不必手动监听滚动或 resize 事件并计算高度、宽度等信息。

TIP

该 API 注册的回调函数是异步执行的,会将其加入(宏)任务队列,不会阻塞主线程。

语法

js
// 创建一个 IntersectionObserver 实例
const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('元素进入视口');
    } else {
      console.log('元素离开视口');
    }
  });
}, {
  root: null,         // 计算交叉部分时作为边界的根元素,未传入或传入 null 时默认为顶级文档的视口。
  rootMargin: '0px',  // 计算交叉部分时添加到边界盒的矩形偏移量,默认为 '0px'。
  threshold: 0      // 计算交叉部分时触发回调函数的交叉比例阈值,可以为单个数值或数值数组。
                    // 默认为 0,也就是说当目标元素任何部分进入或离开视口时,回调函数都会触发。
});

// 观察目标元素
const targetElement = document.querySelector('.target');
observer.observe(targetElement);

// 停止观察目标元素
observer.unobserve(targetElement);

// 停止观察所有目标元素
observer.disconnect();

// takeRecords 方法返回一个包含所有未处理的 IntersectionObserverEntry 对象的数组
// 表示目标元素的当前交叉状态。通常用于在回调函数之前立即获取交叉状态,避免遗漏状态。
const entries = observer.takeRecords();
entries.forEach(entry => {
  console.log(entry.isIntersecting ? '当前可见' : '当前不可见');
});

配置项

threshold 很好理解,它是一个数值或数值数组,表示交叉部分所占比例。下边我们来演示一下另外两个配置项 rootrootMargin

vue
<script setup>
  const toast = useToast();
  onMounted(() => {
    const observer = new IntersectionObserver(
      entries => {
        if (entries[0].isIntersecting) {
          toast.add({ title: `目标元素进入` });
        } else {
          toast.add({ title: `目标元素离开` });
        }
      },
      {
        root: document.getElementById('scrollContainer'), // 使用自定义容器作为 root
        rootMargin: '100px 0px' // 扩展视口区域,在顶部额外增加 100px
      }
    );

    observer.observe(document.querySelector('.target'));
  });
</script>

<template>
  <div
    id="scrollContainer"
    style="height: 300px; overflow: auto; border: 1px solid white"
  >
    <UNotifications />
    <div style="height: 600px">
      <div
        class="target"
        style="margin-top: 400px; height: 100px; background-color: lightblue"
      >
        目标元素
      </div>
    </div>
  </div>
</template>

在这个例子中,我们将 root 设置为一个特定的容器 #scrollContainer,而不是默认的视口。这意味着目标元素的可见性是相对于这个容器来检测的。

使用 rootMargin 来调整 root 的检测区域。 例如,如果你想在目标元素距离 root 还有一段距离时就开始加载(如懒加载图片),可以通过设置 rootMargin 来扩大视口的检测区域。

应用场景

  • 懒加载:当图片进入视口时再加载图片
  • 无限滚动:当滚动到底部时加载更多数据
  • 曝光统计:统计广告曝光次数
  • 滚动动画:当元素进入视口时播放动画

基于 Apache-2.0 许可发布