[ js ]複数の要素を監視して、Viewportに入ったら表示させる方法[IntersectionObserver]

複数の要素を監視して、Viewportに入ったら表示させる方法

この記事は、以下の記事の続編となる記事です!ぜひ前回の記事と見比べながらご覧になると、より分かりやすいかと思います。
[ js ]「スクロールして特定の位置をすぎたらふわっと表示」を簡単に実装する方法[IntersectionObserver]

IntersectionObserver で、複数の要素を監視する

前回の記事では、1つの IntersectionObserver に対して、1つの要素を監視、
「その要素がViewportに入ったら」という条件で
ふわっと表示させる、という事について書きました。

実際の制作では、1つの要素だけではなく、Webサイト上で
スクロールに応じて複数の要素を次々表示させる、といった使い方をきっとするはずです。

1つの IntersectionObserver に対して、1つずつの要素の監視しかできないと、
その度に新しい IntersectionObserver を作成することになりますが、
現実的ではない様に思えますよね。
そんな不便な仕様のわけがないと。そんなふうに思うはずです。

ではどうすれば良いのか。
次の様にすれば、1つの IntersectionObserver で、複数の要素の監視が可能になります。

1つの IntersectionObserver で、複数の要素を監視するコード

let myTarget = document.querySelectorAll('.is-target'); // ...ターゲットとする対象を指定

// makeObserver(); の実行(もちろん関数名は何でも良いです)
makeObserver();

// IntersectionObserverを作成する関数
function makeObserver() {
  let myObserver;

 // IntersectionObserverのオプション設定 ...3種類の値が渡せます
  let myOptions = {
    root: null,
    rootMargin: '0px 0px',
    threshold: '0'
  };
  
  // IntersectionObserverの作成
  myObserver = new IntersectionObserver(myIntersect, myOptions);

  // 複数の対象要素を監視
  for (let n = 0; n < myTarget.length; n++) {
    myObserver.observe(myTarget[n]);
  }
}

// 条件を満たした実行される関数
function myIntersect(entries, myObserver) {
  entries.forEach((entry, n) => {

  const nowElement = entry.target;

    if (entry.isIntersecting) {
      nowElement.classList.remove('is-target');
      nowElement.classList.add('is-animation');
    } else {
      nowElement.classList.remove('is-animation');
      nowElement.classList.add('is-target');
    }
  });
}

基本的には、前回の記事のコードと似た様な構成ですが、変更点としては
21行目~23行目です。
1行目で指定している通り、対象とする複数の要素には、html上で
あらかじめ、is-target というclassをそれぞれに付けておきます。

myTarget には、配列として対象の各要素が入る事になるので、
それを21行目~23行目のfor文で、
配列内の要素の数の分だけ監視対象として設定している記述になります。

実行されるごとに異なるアニメーションを実行するには

上記の様に、
複数の要素を監視対象とした場合、
そのそれぞれの要素にアクセスしたいケースがあると思います。

その場合は、上記コードの27行目〜末尾にある様に
callback関数の方を書き換えれば、アクセスが可能になります。

現在実行されている要素を知るには

複数の要素をターゲットにしている中で、
「現在実行されている要素」を知るには、30行目の様に
const nowElement = entry.target;
とすれば良いです。

forEachが動いている中で参照されていると言うことは、
その瞬間、実行されている挙動の対象が取得できる、と言うことですね。

n回目の実行を知るには

また、28行目にある通り、forEach() の第二引数を指定しておくと、
「ループの何回目の処理なのか」
という値を取得することができます(1回目が0, 2回目が1…)

n回目の実行、という情報にアクセスできたとして、
これをどう活用するかといえば、

例えばcssアニメーション側で、
末尾に0, 1, 2… のように連番を持ったclassを作成しておき、
次の様に指定してあげれば、時間差でアニメーションを実行する処理ができたりします。

(...省略)
if (entry.isIntersecting) {
  nowElement.classList.remove('is-target');
  nowElement.classList.add('is-animation'+n);
} else {
  nowElement.classList.remove('is-animation'+n);
  nowElement.classList.add('is-target');
}
(...省略)

↑この様にしておくと、1回目の実行の際には
is-animation0 が付与され、
2回目の実行の際は
is-animation1 が付与され…
という形になりますので、柔軟な処理が可能になるかと思います。
(例えば、is-animation1 は左からスライドインするアニメーションを指定しておき、is-animation2 の場合は右からスライドインさせる、などなど)

もちろん、連番の class を用意しておくことが
あまり良しとされない案件もあると思います。

その場合は別の処理を書いても良いと思いますし、またその場合でも、
「n回目の実行」という情報があると、分岐などしやすいかと思われます。

IntersectionObserverのサンプル

実際に IntersectionObserver が動いているサンプルを作成してあります。
次のリンクより、確認することができます。
IntersectionObserverのサンプル

この記事のまとめ

前回の記事の続編として、
1つの IntersectionObserver で、複数の対象要素を監視するにはどうすれば良いのか?

といった事を中心に、記事を書いてみました。

実際に私自身も
IntersectionObserver を使ってみて、
その便利さをとても感じています。大げさですが、ある種の感動すら覚えました。

また、前回も書きましたが、
lazyload と組み合わせるケースなどにおいて
Googleが公開している記事内でも使用が推奨されている手法ですので、
その点においても知っておくと、何かと良いかと思います。

当サイトでは、今後もWeb制作に関するさまざまな記事を更新していく予定です。

※この記事は内容の品質向上を目的に、随時更新を行う場合があります。