跳转到内容

IntersectionObserver

简介

Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。传统上,要实现类似功能需要通过监听scroll事件并调用Element.getBoundingClientRect()来检查元素位置,这种方式性能较差。而Intersection Observer API可以有效解决这个问题,它允许我们注册一个回调函数,当目标元素的可见性发生变化时自动执行。

核心概念

在深入了解API之前,我们需要理解几个关键概念:

  • 目标元素(Target Element):被观察的元素,我们需要监听它与根元素的相交情况
  • 根元素(Root Element):观察的目标元素的祖先元素,其视窗用于检查目标的可见性。默认为浏览器视口(viewport)
  • 根边界框(Root Boundary):根元素的矩形区域,用于检查目标元素的可见性
  • 阈值(Thresholds):一个数字数组,表示目标元素与根元素相交程度的百分比,当达到这些阈值时会触发回调函数

基本语法

创建一个IntersectionObserver实例的基本语法如下:

js
const observer = new IntersectionObserver(callback, options);

其中:

  • callback:当元素可见性变化时触发的回调函数
  • options:配置对象,包含以下属性:
    • root:用于观察的根元素,默认为浏览器视口
    • rootMargin:根元素边界盒的偏移值,类似于CSS的margin属性
    • threshold:一个数字或数组,表示触发回调的阈值

实际应用

懒加载图片

Intersection Observer最常见的用途之一是实现图片懒加载:

js
const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      const src = img.dataset.src;
      
      img.src = src;
      img.classList.remove('lazy');
      
      observer.unobserve(img);
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => {
  imageObserver.observe(img);
});

无限滚动

js
const sentinel = document.querySelector('#sentinel');
const options = {
  rootMargin: '100px'
};

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loadMoreItems();
    }
  });
}, options);

observer.observe(sentinel);

动画触发

js
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('animate');
      observer.unobserve(entry.target);
    }
  });
}, {
  threshold: 0.5
});

document.querySelectorAll('.animate-on-scroll').forEach(el => {
  observer.observe(el);
});

配置选项详解

root选项

指定观察的根元素,默认为浏览器视口:

js
// 使用默认的viewport作为根元素
const observer1 = new IntersectionObserver(callback);

// 使用特定元素作为根元素
const root = document.querySelector('#scrollArea');
const observer2 = new IntersectionObserver(callback, { root });

rootMargin选项

允许我们为根元素的边界框添加一定的"边距",类似于CSS的margin属性:

js
const options = {
  rootMargin: '10px 20px 30px 40px' // 上、右、下、左
};

const observer = new IntersectionObserver(callback, options);

threshold选项

定义触发回调的阈值,可以是单个值或数组:

js
// 当目标元素50%可见时触发
const options1 = {
  threshold: 0.5
};

// 在多个阈值点触发
const options2 = {
  threshold: [0, 0.25, 0.5, 0.75, 1.0]
};

实例方法

observe()

开始观察一个目标元素:

js
const observer = new IntersectionObserver(callback);
const target = document.querySelector('#target');
observer.observe(target);

unobserve()

停止观察一个目标元素:

js
observer.unobserve(target);

disconnect()

停止观察所有目标元素:

js
observer.disconnect();

takeRecords()

返回所有观察目标的IntersectionObserverEntry对象数组:

js
const entries = observer.takeRecords();

回调函数参数

回调函数接收两个参数:

js
const callback = (entries, observer) => {
  entries.forEach(entry => {
    // entry 是 IntersectionObserverEntry 对象
    console.log(entry.intersectionRatio); // 相交比例
    console.log(entry.isIntersecting);    // 是否相交
    console.log(entry.target);            // 目标元素
  });
};

实际应用考虑

浏览器兼容性

Intersection Observer API在现代浏览器中有良好的支持,但在一些老版本浏览器中可能需要polyfill:

js
// 检查浏览器支持
if ('IntersectionObserver' in window) {
  // 使用Intersection Observer
} else {
  // 降级处理方案
}

性能优化

  • Intersection Observer本身就是为性能优化而设计的
  • 避免在回调函数中执行复杂操作
  • 及时使用unobserve()或disconnect()停止观察不需要的元素

与其他技术对比

特性Intersection Observer传统scroll事件监听
性能高(异步、批量处理)低(频繁触发)
实现复杂度简单复杂
浏览器原生支持现代浏览器所有浏览器
精确度取决于实现

相关资源