Intersection Observer

要素がビューポートに入ったことを非同期で検知する Web API

APIパフォーマンス

Intersection Observer とは

Intersection Observer は、要素がビューポート (または指定したコンテナ) に入った/出たことを非同期で検知する Web API である。スクロールイベントのリスナーと異なり、メインスレッドをブロックせずに高パフォーマンスで動作する。

scroll イベントとの比較

観点 scroll イベント Intersection Observer
パフォーマンス メインスレッドで実行 (重い) 非同期 (軽い)
スロットリング 自前で実装が必要 不要
精度 ピクセル単位で計算 交差率 (threshold) で検知

基本的な使い方

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target); // 一度だけ検知
    }
  });
}, { threshold: 0.1 }); // 10% 見えたら発火

document.querySelectorAll('.lazy').forEach(el => observer.observe(el));

画像の遅延読み込み

const imgObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target as HTMLImageElement;
      img.src = img.dataset.src!;
      imgObserver.unobserve(img);
    }
  });
}, { rootMargin: '200px' }); // 200px 手前で読み込み開始

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

rootMargin: '200px' で、ビューポートの 200px 手前から読み込みを開始し、スクロール時に画像が表示される前にロードを完了する。

無限スクロール

const sentinel = document.getElementById('sentinel')!;

const observer = new IntersectionObserver(async ([entry]) => {
  if (entry.isIntersecting) {
    const items = await fetchNextPage();
    renderItems(items);
  }
});

observer.observe(sentinel); // リストの末尾に配置した要素を監視

スクロール連動アニメーション

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('fade-in');
    }
  });
}, { threshold: 0.2 });

document.querySelectorAll('.section').forEach(el => observer.observe(el));
.section { opacity: 0; transform: translateY(30px); transition: all 0.5s ease; }
.section.fade-in { opacity: 1; transform: translateY(0); }

React での使い方

function useInView(ref: RefObject<HTMLElement>) {
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const observer = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting));
    observer.observe(ref.current);
    return () => observer.disconnect();
  }, [ref]);
  return inView;
}

オプション

オプション 説明
root 交差を判定するコンテナ (デフォルト: ビューポート)
rootMargin root の余白 ('200px' で手前から検知)
threshold 交差率 (0.1 = 10% 見えたら発火)

Intersection Observer の理解を深めるには関連書籍が参考になる。

関連用語