scrollイベントより軽い!IntersectionObserverを初めて使ってみた


スクールのコーディングテスト対策で、スクロールアニメーションを実装しました。

「ページをスクロールすると要素がふわっと現れる」あの動き、どうやって作るんだろう?とずっと気になっていました。調べてみると IntersectionObserver というAPIを使うと実現できることがわかり、実装してみました。この記事ではその仕組みと実装方法をまとめます。

IntersectionObserver とは?

一言でいうと、要素が画面内に入ったかどうかを監視するAPIです。

スクロールイベント(scroll)を使う方法もありますが、スクロールのたびに処理が走るので負荷が高くなりがちです。IntersectionObserver はブラウザが効率よく監視してくれるので、パフォーマンスの面でも優れています。

scroll イベントとの違い

scroll イベント IntersectionObserver
処理のタイミング スクロールのたびに毎回 要素が画面に入ったときだけ
パフォーマンス 重くなりやすい 軽い
コードの量 多め シンプル

実装したコード

HTML

アニメーションさせたい要素に js-fade クラスをつけます。

<div class="js-fade">
  <p>スクロールしたら表示される要素</p>
</div>

<div class="js-fade">
  <p>こちらも表示される要素</p>
</div>

CSS

最初は非表示にしておいて、is-visible クラスがついたら表示されるようにします。

/* 初期状態:非表示 */
.js-fade {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

/* is-visibleクラスがついたとき:表示 */
.js-fade.is-visible {
  opacity: 1;
  transform: translateY(0);
}

translateY(20px) で少し下にずらしておき、表示されるときに元の位置に戻ることで「ふわっと上がってくる」動きになります。

JavaScript

const fadeElements = document.querySelectorAll('.js-fade');

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
    }
  });
});

fadeElements.forEach((el) => {
  observer.observe(el);
});

コードの解説

① 監視する要素をまとめて取得する

const fadeElements = document.querySelectorAll('.js-fade');

querySelectorAlljs-fade クラスのついた要素をすべて取得します。

② IntersectionObserver を作る

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
    }
  });
});

entries は監視している要素の配列です。entry.isIntersectingtrue のとき、つまり要素が画面内に入ったときに is-visible クラスを追加します。

③ 各要素を監視対象に登録する

fadeElements.forEach((el) => {
  observer.observe(el);
});

取得した要素を1つずつ observe() に渡して監視をスタートします。

詰まったところ

CSS に transition を書く場所

最初、is-visible の方に transition を書いてしまい、アニメーションが動きませんでした。

transition初期状態(.js-fade)の方に書くのが正しいです。クラスが切り替わる前から transition が設定されていないと、変化をアニメーションとして認識してくれません。

querySelectorAll の結果は配列ではない

querySelectorAll の戻り値は NodeList という配列に似たオブジェクトで、そのままでは forEach が使えない場合があります。ただし現代のブラウザでは NodeList でも forEach が使えるので今回はそのまま使っています。

まとめ

  • IntersectionObserver は要素が画面内に入ったタイミングを検知できる
  • scroll イベントより軽くてシンプルに書ける
  • transition は初期状態のクラスに書く
  • isIntersectingtrue のときにクラスを追加してアニメーションを発火させる