今回は画像の遅延読み込み(Lazy Load)です。画像の遅延読み込みは、最初のページの読み込み時間、最初のページのデータ量、システムリソースの使用量を減らすことにより、パフォーマンスの向上に期待ができます。「Intersection Observer API 」を使って画像の遅延読み込みをしてみます。
Intersection Observer API
Intersection Observer API (交差監視 API) は、ターゲットとなる要素が、祖先要素もしくは文書の最上位のビューポートと交差する変更を非同期的に監視する方法を提供します。
ブラウザ間の互換性

スクロールしてボックスの背景色を変える
MDN Intersection Observer API に良いコードサンプルがあったので、このサンプルを使用します。
サンプルでは、監視要素が viewport(ルート要素) と交差する(見えるか見えないか)たびにコールされるコールバック関数(要素の背景色を変える)を構成しています。
環境
html
1 2 3 |
<div id="practice-area"> <div id="box"></div> </div> |
css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#practice-area { height: 1500px; } #box { background-color: rgba(40, 40, 190, 255); border: 4px solid rgb(20, 20, 120); width: 350px; height: 350px; display: flex; align-items: center; justify-content: center; padding: 20px; margin-top: 1000px; } |
javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
var prevRatio = 0.0; var increasingColor = "rgba(40, 40, 190, ratio)"; var decreasingColor = "rgba(190, 40, 40, ratio)"; window.addEventListener("load", () => { // Intersection observer のオプション let options = { // target が見えるかどうかを確認するための // ビューポートとして使用される要素 root: null, // デフォルトはブラウザーのビューポート // root のマージン(既定ではすべてゼロ) rootMargin: "0px", // 指定した「閾値」に対してコールバックが呼び出される // 単一の数値もしくは数値の配列で、 // observer のコールバックを実行する target が、 // どのくらいの割合で見えているかを示す threshold: buildThresholdList() }; // target が IntersectionObserver に指定された「閾値」を満たす度に // 呼び出されるコールバック関数 // コールバックは IntersectionObserverEntry オブジェクトとオブザーバーのリスト // を受け取る let handleIntersect = (entries, observer) => { for (let entry of entries) { if (entry.intersectionRatio > prevRatio) { entry.target.style.backgroundColor = increasingColor.replace("ratio", entry.intersectionRatio); } else { entry.target.style.backgroundColor = decreasingColor.replace("ratio", entry.intersectionRatio); } prevRatio = entry.intersectionRatio; } } if ("IntersectionObserver" in window) { // コンストラクターを呼び出して intersection observer を作成 let observer = new IntersectionObserver(handleIntersect, options); // 監視する target 要素 let target = document.getElementById("box"); observer.observe(target); } }, { capture: false, once: true, passive: true }); // 「閾値」を生成 function buildThresholdList() { let thresholds = []; let numSteps = 20; for (let i = 1.0; i <= numSteps; i++) { let ratio = i / numSteps; thresholds.push(ratio); } thresholds.push(0); return thresholds; } |
MDN にも注意事項として記載がありますが、コールバックはメインスレッドで実行されます。時間を要する処理の場合、Window.requestIdleCallback() などを使用してパフォーマンスを上げる工夫が必要になります。
画像の遅延読み込み
それでは上記のコードに、本題の画像の遅延読み込みのコードを足します。Google Developers で 「CSS での画像の遅延読み込み処理」の良質サンプルコードを見つけることができたので、これを参考にします。
ページが最初に読み込まれた時のオフスクリーン画像は、プレースホルダイメージを参照するようにします。
プレースホルダイメージ

そして、スクロールして監視要素が viewport に入ったときに、表示させたい画像を参照するようにします。
表示させたい画像

javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
document.addEventListener("DOMContentLoaded", () => { let lazyBackgrounds = Array.prototype.slice.call(document.querySelectorAll(".lazy-background")); if ("IntersectionObserver" in window) { let observer = new IntersectionObserver((entries, observer) => { for (let entry of entries) { // true: target要素が交差状態に移行 // false: 交差状態から移行 if (entry.isIntersecting) { entry.target.classList.add("visible"); // target の監視を解除 observer.unobserve(entry.target); } } }); for (let lazyBackground of lazyBackgrounds) { observer.observe(lazyBackground); } } }, { capture: false, once: true, passive: true }); |
動作(CodePen)
Intersection Observer API での背景色の変更 + 画像の遅延読み込み動作を下の CodePen で確認できます。
Google Developers の CSS でのイメージ処理は、Intersection Observer API だけではなくドキュメント オブジェクト モデル(DOM)、CSS オブジェクト モデル(CSSOM)、レンダリング ツリーといったブラウザ内部の基本動作についても紹介されており、一見の価値ありのコンテンツです。
Intersection Observer API は便利ですが、ブラウザによっては polyfill する必要があります。モダンな環境で開発したい方はこちらを参考にしていただければと思います。