#1)はじめに
スクロールに追従して、
- 画像の遅延ロード
- YouTubeの自動再生
- コンテンツの追加読み込み
といったことを行いたい。
このようなケースにおいて、今まではスクロールイベントをフックとして実装することがほとんどでした。しかし、頻繁に実行されるスクロールイベントは、ブラウザにとって優しい処理とは言えず、特にスマホではスクロール詰まり(Scroll Jank)を引き起こしてしまうこともありました。
そこで登場する、比較的新しいAPIがIntersection Observerです。
今回はその利用方法について模索してみます。
#2)Intersection Observerとは
その名の通り「Intersection(要素間交差)」を「Observe(監視)」するAPI。
任意の要素(DOM)同士の交差を監視することが出来ます。
デフォルトでviewport(見えている範囲)とある要素が交差=ある要素が見えたら何かする、というものです。
// オプション
const options = {
// ルートとして指定するDOM(無ければviewport)
root: document.querySelector('.root'),
// 上下100px、左右20px手前で発火
rootMargin: "100px 20px",
// 交差領域が50%変化するたびに発火
threshold: [0, 0.5, 1.0]
};
/// 初期化
const observer = new IntersectionObserver(callback, options);
targets.forEach(function(img) {
// 監視の開始
observer.observe(img);
});
// コールバック
function callback(entries, object) {
entries.forEach(function(entry, i) {
// 交差していない
if (!entry.isIntersecting) return;
// ターゲット要素
const img = entry.target;
// 遅延ロードなど、行いたいことの処理
loading(img);
// 監視の解除
object.unobserve(img);
});
};
##root(任意)
デフォルトではdocumentすなわちviewport。
見えている範囲とターゲットが交わる(すなわち可視状態にある)時に動作している。
これを変更することで別ターゲットと交わった際の動作を指定できる。
##rootMargin(任意)
指定についてはCSS同様。上記の例では上下100px、左右20px手前でイベントが発生する。
画像の遅延ロードを行う際有効で、例えば【rootMargin: '500px 0px 0px 0px'】と指定すれば、要素が現れるより500px先に画像のロードが始まることになり、画像の遅延ロード本来の違和感を少なくすることが出来る。
##threshold(任意)
例のように【threshold: [0, 0.5, 1.0]】とすれば、交差領域が50%変化するたびにコールバックすることが出来る。
#3)対応ブラウザについて
Can I use:IntersectionObserver
IE11、iOS Safariは全滅。Edgeは16移行対応となっています。
割と新し目のAPIとは言え、IE11やiPhoneを切ることはなかなか厳しいのではないかと思います。
そこで、下記のDEMOを参考に、Intersection Observer未対応のブラウザにはPolyfill(新しい技術を使えない古いブラウザでも代替の手段を使って同等の効果を得るための拡張)で対応してみたいと思います。
Lazy Image Loading (Using Intersection Observer API [including polyfill])
例えば画像の遅延ロードで言えば、古いブラウザであっても画像が見えないとあっては大問題なので、Polyfillを用いて古いブラウザであっても同等の効果を得ようというのです。
#4)様々なDEMO
画像の遅延ロード以外のDEMOをあまり見かけないので、実用的かもしれないDEMOを、Polyfillありなしで作成してみました。
- DEMO1:画像の遅延ロード
- DEMO2:画像の遅延ロード(Polyfillあり)
- DEMO3:YouTubeの自動再生
- DEMO4:YouTubeの自動再生(Polyfillあり)
- DEMO5:コンテンツの追加読み込み
- DEMO6:コンテンツの追加読み込み(Polyfillあり)
(Polyfillあり)のDEMOについては、IE11、iOS Safari(Desktop/iPhone)でも問題なく動作していることがわかります。iPhoneやIE11のシェアを考えるに、当面の間はPolyfillありのものを使った方が良さそうです。
#5)参考リンク
- Intersection Observer を用いた要素出現検出の最適化 | blog.jxck.io
- [JS]これなら簡単で便利!要素がビューポートに表示されているかを判定できる -Intersection Observer | コリス
- Intersection Observer API - Web API インターフェイス | MDN
- 無限スクロールをIntersectionObserver APIで実装する最新テクニック - WPJ
- GitHub - w3c/IntersectionObserver: API Sketch for Intersection Observers
- Can I use:IntersectionObserver
なお、過去の記事などでPolifillのURLをhttps://github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill
と紹介いるものが多いが、これは現在404となっている。
今回のDEMOに内包したMinifyしたPolifillは、下記のものを利用している。
https://github.com/w3c/IntersectionObserver
#6)おまけ
「要素が見えたら何かする」JavaScriptについて色々調べてみましたが、「Lozad.js」
ってやつが何気に良さそうでした。
俺的LazyLoadの決定版、Lozad.jsを紹介する - Qiita
Intersection Observerを使いやすくした軽量なJSで、Polyfillを使えば未対応ブラウザでも使用可能、画像、iframeの遅延ロードもお手軽にできそうです。