no-image

IntersectionObserverで要素が画面内に入ってきたかを判定する

ウェブにおいて、ある要素が他の要素と重なっているかどうか判定したいことは、よくあります。代表的な例としては画像の遅延読み込みがあります。画像が画面内に入ってきてから読み込むことで、負荷を抑えるテクニックです。しかし従来の方法は、パフォーマンスへの悪影響や、コードの複雑化など、様々な問題を含んでいました。

そこで、近年になって新しく導入されたIntersectionObserverを使えば、パフォーマンスへの影響を気にすることなく、要素の交差判定を簡単に実装することができます。

要素と要素の交差判定

画像の遅延読み込みやスクロールアニメーションを実装する際、「要素が画面内に入ってきたか」という情報が重要になります。これは「要素と他の要素(画面の場合はviewport)の交差判定」と言い換えることもできます。

要素の交差判定は、scrollイベントを監視し、scrollTopなどのプロパティを使って計算することで実現することができます。しかしこの手法には問題があります。scrollイベントは極めて高頻度で発生しますし、scrollTopなどの表示情報を取得するAPIは、ブラウザに強制的かつ同期的なレイアウトの処理を発生させます。つまり、この手法での実現は「重く」なるのです。

そこで、こういった交差(Intersection)処理を実現するために、IntersectionObserverという仕組みが用意されました。IntersecitonObserverを使えば、パフォーマンスへ悪影響を与えることなく、簡単に実装することができます。

対応ブラウザ

IntersectionObserverは、現在のところ一部のブラウザしか対応していません。詳しくはCan I Useをご覧ください。

IntersectionObserverのAPI

IntersectionObserverのAPIは非常にシンプルです。IntersectionObserverオブジェクトを作成し、交差を監視したい要素をobserveするだけです。

// IntersectionObserverオブジェクトを作成する。
// 交差時に実行するコールバック関数を渡す。
const observer = new IntersectionObserver((entries) => {
  for(const e of entries) {
    console.log(e);
  }
});

// 監視したい要素をobserveする。
observer.observe(document.querySelector('.target'));

これだけで交差判定を実現できます。デフォルトでは、画面内に入ってきたときに、または完全に下側に出て行ったときにコールバック関数が呼ばれます。

また、ひとつのIntersectionObserverで複数の要素をobserveすることもできます。複数の要素を監視した場合は、コールバック関数のentriesに複数の要素が入ります。

監視を中止したい場合はunobserve(target)メソッド、全ての監視を中止したい場合はdisconnect()メソッドを呼び出します。

IntersectionObserverEntry

コールバック関数の第一引数には、監視した要素の数だけのIntersectionObserverEntryオブジェクトが入ります。IntersectionObserverEntryには様々なプロパティが含まれます。

// IntersectionObserverオブジェクトを作成する。
const observer = new IntersectionObserver((entries) => {
  for(const e of entries) {
    console.log(e.isIntersecting);
    console.log(e.intersectionRatio);
    console.log(e.intersectionRect);
  }
});

// 監視したい要素をobserveする。
observer.observe(document.querySelector('.target'));

isIntersectingプロパティは交差しているかどうかのブール値です。intersectionRatioは交差している量を0.0〜1.0の範囲で示します。intersectionRectは交差している領域の矩形オブジェクトです。他にも様々なプロパティがありますが、詳しくはMDNをご覧ください。

オプション

IntersectionObserverにはいくつかのオプションが存在します。オプションを指定するには、コンストラクタの第二引数にオブジェクトを渡します。

// オプションを指定する。
const options = {
  root: document.querySelector('.root'),
  rootMargin: '5%',
  threshold: [0, 0.5, 1.0]
}

// オプションとともにIntersectionObserverオブジェクトを作成する。
const observer = new IntersectionObserver((entries) => {
  for(const e of entries) {
    console.log(e);
  }
}, options);

// 監視したい要素をobserveする。
observer.observe(document.querySelector('.target'));

rootはルート(交差判定のベース)となる要素を指定します。デフォルトではviewportです。rootを明示的に指定することで、viewport以外との交差判定を実現することができます。

rootMarginはrootからのマージンを指定します。マージンを指定することで、rootと交差する前に発火させることができます。方法はCSSのmarginとほぼ同じですが、単位はpxか%である必要があります。デフォルトは’0px 0px 0px 0px’です。

thresholdはコールバック関数が呼ばれるタイミングを指定するオプションです。単一の値か、または複数の値を指定します。デフォルトでは0です。0はほんの少しでもrootに入ってきたときのことを示します。逆に1にすると、完全にrootに入ってきたときのことを示します。例えば[0, 0.5, 1.0]と指定すれば、「入ってきたとき」「真ん中まできたとき」「完全に入ったとき」の3回コールバック関数が呼び出されます。

デモ

デモを開く