ウェブサイトの要件において、スクロールしていて画面内に入ったら処理を実行するなどの機能はよく見かけますが、jQueryなどを使った実装は少し煩雑になりがちで、保守性もあまり良くないかと思います。
そんな場合にJavaScriptのAPIで用意されている「IntersectionObserverAPI」を使うことで簡単かつシンプルに実装することができ、細かい調整なども簡単にできるようになります。今回はこのIntersectionObserverAPIを使って、ビューポート内の要素の表示を検知してみたいと思います。
まずはこちらのHTMLとCSSをサンプルとして使います。検知対象の要素には「observe_target」というclass名をつけています。そして検知された場合には「observed」というclass名が目印として付くようにしています。
【HTML / CSS】※一部抜粋
<section> <span class="observe_target">ターゲット要素</span> </section> <section> <span class="observe_target">ターゲット要素</span> </section> <section> <span class="observe_target">ターゲット要素</span> </section> section { height: 100vh; } .observe_target { color: #000; transition: all 1.2s ease 0s; } .observe_target.observed { color: #f00; }
続いてJavaScriptで要素の検知を監視する処理を作成していきます。共通の関数としてまとめておくことでいろんなところで使い回すことができるので便利です。使い分けができるように引数には対象となる要素を判別できる値を入れられるようにしておきます。
【JavaScript】
const doObserve = (element) => { const targets = document.querySelectorAll(element); const options = { root: null, rootMargin: '0px', threshold: 0 }; const observer = new IntersectionObserver((items) => { items.forEach((item) => { if (item.isIntersecting) { item.target.classList.add('observed'); } else { item.target.classList.remove('observed'); } }); }, options); Array.from(targets).forEach((target) => { observer.observe(target); }); }; doObserve('.observe_target');
まずは対象となる要素をquerySelectorAll()を使って、class名から全て取得します。もちろん単一の場合にはIDやタグなどで取得することも可能です。これらはNodelist形式の値で変数内に入れておきます。
続けて、IntersectionObserverAPIのオプションを指定します。詳しくは公式のドキュメントを参考に、必要に応じて調整します。ここで設定したオプションは、この後に作成するIntersectionObserverインスタンスの第二引数に設定されます。オプション内で「root」のキーを「null」に設定することでビューポート内に入ったかどうかの検知ができるようになります。
そして、IntersectionObserverインスタンスを作成することで、要素のビューポート内表示の検知ができるようになります。第二引数には先ほどのオプションが入りますが、第一引数にはコールバック関数を指定できます。このコールバック関数内で、検知された時の処理を記述しておきます。詳細については後ほど詳しく見ていきます。最後に、初めに取得した対象となる要素に対して、それぞれIntersectionObserverAPIを使って検知を行なっていきます。observe()メソッドの引数に対象となる要素が入る形になります。ここではclass名で取得していますので、対象となるclass名を入れています。
先ほど触れました、検知された時に実行される処理についてですが、IntersectionObserverを作成し、要素に対しての検知を行なっていくと、その検知情報のオブジェクトが取得できるようになります。その中に「isIntersecting」というキーがあり、対象要素が検知されると値がtrue、外れるとfalseを返すというのを使って条件分岐させることで、検知されたタイミングでの処理が可能になります。ここでは指定したclass名の追加・削除を行なっています。
これでスクロールした際にビューポート内での要素の表示を検知できるようになりました。実際のサンプルは下記で確認することができます。
See the Pen
IntersectionObserverAPISample by designsupply (@designsupply)
on CodePen.
また、複数要素の際に、1要素ずつ処理をずらしたシーケンシャルのような形にする場合には、下記のように要素の数に対応するインデックス番号を取得し、setTimeoutなどで少しずつ処理の開始を遅らせることで実現可能です。
【JavaScript】
const doObserve = (element) => { const targets = document.querySelectorAll(element); const options = { root: null, rootMargin: '0px', threshold: 0 }; const observer = new IntersectionObserver((items) => { items.forEach((item, index) => { if (item.isIntersecting) { setTimeout(() => { item.target.classList.add('observed') }, index * 200) } else { item.target.classList.remove('observed') } }) }, options) Array.from(targets).forEach((target) => { observer.observe(target) }) }; doObserve('.observe_target');
スクロールに対応したアニメーションなどはこの処理を使って、検知を行い、CSSでアニメーションを用意すると簡単に実装することができますね。オプションや処理の切り分けなどを行うことで様々な機能要件にも応用できそうなのがいいですね。ぜひ試してみてください。
(参考にさせていただいたサイト)
WebAPI「Intersection Observer API」