Back

Intersection Observerを使用してビューポートへの要素の進入を検出する

Intersection Observerを使用してビューポートへの要素の進入を検出する

スクロールイベントリスナーによる要素の可視性追跡は、ウェブサイトのパフォーマンスを著しく低下させる可能性があります。スクロールのたびに複数のイベントが発生し、それぞれがgetBoundingClientRect()を呼び出して、ブラウザに高コストなリフローを強制します。Intersection Observer APIは、この問題をエレガントに解決し、要素がビューポートに進入または離脱する際の検出に対してネイティブなブラウザ最適化を提供します。

重要なポイント

  • Intersection Observerは、スクロールイベントリスナーによるパフォーマンスボトルネックを解消する
  • APIは非同期で動作し、メインスレッドのブロッキングを防ぐ
  • 1つのオブザーバーで複数の要素を効率的に監視できる
  • ネイティブなブラウザ最適化により、手動計算よりも優れたパフォーマンスを提供する

従来のスクロールイベントが不十分な理由

スクロールイベントリスナーは、スクロール中に継続的に発火し、しばしば1秒間に60回以上トリガーされます。getBoundingClientRect()を呼び出す各イベントハンドラーは、ブラウザにレイアウトの再計算を強制し、ぎこちないスクロール体験を生み出します。広告、アナリティクス、遅延読み込みなど、複数のライブラリが独立して可視性を追跡する場合、パフォーマンスへの影響は劇的に複合されます。

Intersection Observerアプローチは、これらの計算をメインスレッドから移動させ、ブラウザが交差チェックをいつ、どのように実行するかを最適化できるようにします。

Intersection Observerの基本概念の理解

Intersection Observer APIは、ターゲット要素がルート要素(通常はビューポート)との境界を横切る際を非同期で監視します。継続的なポーリングの代わりに、可視性の閾値が横切られた場合のみ通知します。

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('Element is visible');
    }
  });
});

observer.observe(document.querySelector('.target'));

ブラウザは全ての交差計算を内部で処理し、IntersectionObserverEntryオブジェクトを含むコールバックを通じて結果を配信します。各エントリはisIntersecting(真偽値)とintersectionRatio(0-1の可視性パーセンテージ)を提供します。

初回オブザーバーの作成

Intersection Observerのセットアップには、コールバック関数とオプションの設定のみが必要です:

const callback = (entries, observer) => {
  entries.forEach(entry => {
    // 利用可能な主要プロパティ
    console.log({
      isVisible: entry.isIntersecting,
      visibilityRatio: entry.intersectionRatio,
      targetElement: entry.target
    });
  });
};

const options = {
  root: null,        // ビューポート
  rootMargin: '0px', // オフセットなし
  threshold: 0.5     // 50%可視
};

const observer = new IntersectionObserver(callback, options);
observer.observe(document.querySelector('.target'));

オブザーバーは複数の要素を同時に追跡できるため、コールバックはエントリの配列を受け取ります。各エントリのisIntersectingは現在の可視性を示し、intersectionRatioは正確な可視性パーセンテージを提供します。

オブザーバーオプションの設定

3つのオプションが交差コールバックの発火タイミングを制御します:

root: 監視するスクロール可能エリアを定義します。nullはビューポートを使用し、任意のスクロール可能要素をカスタムルートとして使用できます。

rootMargin: ルートのバウンディングボックスを拡張または縮小します。CSSマージン構文を使用:"50px"または"10% 0px"。負の値は縮小、正の値は検出エリアを拡張します。

threshold: コールバックをトリガーする可視性パーセンテージ。単一値:0.5(50%)。複数トリガー用の配列:[0, 0.25, 0.5, 0.75, 1]

複数要素の効率的な監視

1つのオブザーバーインスタンスで無制限の要素を監視できます:

const observer = new IntersectionObserver(callback, options);
const targets = document.querySelectorAll('.lazy-load');

targets.forEach(target => observer.observe(target));

// 特定要素の監視を停止
// observer.unobserve(element);

// 全要素の監視を停止
// observer.disconnect();

このパターンは効率を最大化します—ブラウザは数百の要素を監視する単一のオブザーバーを、複数のオブザーバーよりも効率的に最適化します。

実世界での実装例

画像の遅延読み込み

const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      imageObserver.unobserve(img);
    }
  });
}, { rootMargin: '50px' });

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

スクロール時のアニメーショントリガー

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

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

ビュー内での動画の自動再生

const videoObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const video = entry.target;
    if (entry.isIntersecting) {
      video.play();
    } else {
      video.pause();
    }
  });
}, { threshold: 0.5 });

document.querySelectorAll('video').forEach(video => {
  videoObserver.observe(video);
});

まとめ

Intersection Observer APIは、ビューポート検出をパフォーマンスボトルネックから最適化されたブラウザ機能へと変革します。スクロールイベントリスナーをIntersection Observerに置き換えることで、メインスレッドのブロッキングを排除しながら、より精密な可視性制御を獲得できます。スクロールベースの可視性コードの移行を今日から始めましょう—ユーザーのブラウザがその恩恵を受けるでしょう。

よくある質問

はい、要素がDOMに追加された後、新しい要素に対してobserver.observe()を呼び出すだけです。同じオブザーバーインスタンスで、ページライフサイクル中のいつでも追加された要素を監視できます。

コールバックは要素の現在の交差状態で即座に発火します。これにより、別途チェックを行う必要なく、初期の可視性ステータスを常に把握できます。

コールバック内でentry.isIntersectingがfalseであることをチェックしてください。オブザーバーは、閾値設定に基づいて要素が観察エリアに進入および退出する両方の場合に通知します。

モダンブラウザではネイティブサポートされています。Internet Explorerなどの古いブラウザでは、W3Cの公式ポリフィルを使用することで、JavaScriptを通じて同一の機能を提供できます。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay