画像の遅延読み込み

画像がウェブページ上に表示される場合、HTML 内に <img> 要素または CSS 背景画像としてインライン化されます。この投稿では、両方のタイプの画像を遅延読み込みする方法を説明します。

インライン画像

最も一般的な遅延読み込みの候補は、<img> 要素で使用される画像です。インライン画像には遅延読み込み用に 3 つのオプションがあり、ブラウザ間の互換性を最適に保つために組み合わせて使用できます。

ブラウザレベルの遅延読み込みを使用する

Chrome と Firefox はどちらも、loading 属性による遅延読み込みをサポートしています。この属性は、<img> 要素だけでなく、<iframe> 要素にも追加できます。値 lazy は、画像がビューポート内にある場合はすぐに読み込み、ユーザーがその近くでスクロールすると他の画像を取得するようにブラウザに指示します。

ブラウザ サポートの詳細については、MDN のブラウザの互換性の表で loading フィールドをご覧ください。ブラウザで遅延読み込みがサポートされていない場合、属性は無視され、画像は通常どおりすぐに読み込まれます。

この属性をインライン画像に追加すると、ほとんどのウェブサイトでパフォーマンスが向上し、ユーザーがスクロールすることのない画像を読み込むことができます。画像が多数あり、遅延読み込みに対応していないブラウザユーザーが利用できるようにするには、次に説明するいずれかの方法と組み合わせる必要があります。

詳しくは、ウェブ向けのブラウザレベル遅延読み込みをご覧ください。

Intersection Observer を使用する

<img> 要素の遅延読み込みをポリフィルするには、JavaScript を使用して、要素がビューポート内にあるかどうかを確認します。存在する場合は、src(場合によっては srcset)属性に、目的の画像コンテンツへの URL が入力されます。

以前に遅延読み込みコードを記述したことがある場合は、scrollresize などのイベント ハンドラを使用してタスクを完了している可能性があります。この方法はブラウザ間で最も互換性がありますが、最新のブラウザでは、Intersection Observer API を介して要素の可視性を確認する作業をより効率的かつ効率的に行うことができます。

Intersection Observer は、さまざまなイベント ハンドラに依存するコードよりも使いやすく、読みやすくなっています。これは、要素を監視する単調なオブザーバーを登録するだけで済むため、面倒な要素の表示検出コードを記述せずに済みます。あとは、要素が表示されている場合の処理を決めるだけです。遅延読み込みされる <img> 要素に、次の基本的なマークアップ パターンがあるとします。

<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">

このマークアップには、注意すべき 3 つの関連部分があります。

  1. class 属性。JavaScript で要素を選択する際に使用します。
  2. src 属性。ページが最初に読み込まれたときに表示されるプレースホルダ画像を参照します。
  3. data-src 属性と data-srcset 属性。要素がビューポートに配置されると読み込む画像の URL を含むプレースホルダ属性です。

では、JavaScript で Intersection Observer を使用し、このマークアップ パターンで画像を遅延読み込みする方法を見てみましょう。

document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  } else {
    // Possibly fall back to event handlers here
  }
});

ドキュメントの DOMContentLoaded イベントで、このスクリプトは DOM に対してクエリを実行し、クラスが lazy のすべての <img> 要素を取得します。Intersection Observer が使用可能な場合は、img.lazy 要素がビューポートに入ったときにコールバックを実行する新しいオブザーバーを作成します。

Intersection Observer は、最新のすべてのブラウザで利用できます。そのため、loading="lazy" のポリフィルとして使用することで、ほとんどのユーザーが遅延読み込みを使用できるようになります。

CSS の画像

ウェブページで画像を使用する最も一般的な方法は <img> タグですが、CSS の background-image プロパティ(およびその他のプロパティ)から画像を呼び出すこともできます。ブラウザレベルの遅延読み込みは CSS 背景画像には適用されないため、背景画像を遅延読み込みする場合は、他の方法を検討する必要があります。

表示の有無に関係なく読み込まれる <img> 要素とは異なり、CSS での画像読み込み動作は推測に基づいて行われます。ドキュメントと CSS のオブジェクト モデルレンダリング ツリーが作成されると、ブラウザは外部リソースをリクエストする前に、CSS がドキュメントにどのように適用されているかを調べます。外部リソースに関連する CSS ルールが、現在作成されているドキュメントに適用されていないとブラウザが判断した場合、ブラウザはリクエストを行いません。

この投機的動作を利用して、JavaScript を使用して要素がビューポート内にあるタイミングを判断し、背景画像を呼び出すスタイル設定を適用するクラスをその要素に適用することで、CSS での画像の読み込みを遅らせることができます。これにより、イメージは初期読み込み時ではなく、必要になったときにダウンロードされます。たとえば、大きなヒーローの背景画像を含む要素があるとします。

<div class="lazy-background">
  <h1>Here's a hero heading to get your attention!</h1>
  <p>Here's hero copy to convince you to buy a thing!</p>
  <a href="/buy-a-thing">Buy a thing!</a>
</div>

div.lazy-background 要素には通常、一部の CSS によって呼び出されるヒーローの背景画像が含まれます。ただし、この遅延読み込みの例では、ビューポートにあるときに要素に追加された visible クラスを介して、div.lazy-background 要素の background-image プロパティを分離できます。

.lazy-background {
  background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}

.lazy-background.visible {
  background-image: url("hero.jpg"); /* The final image */
}

ここから、JavaScript を使用して(Intersection Observer を使用して)要素がビューポート内にあるかどうかを確認し、その時点で visible クラスを div.lazy-background 要素に追加します。これにより画像が読み込まれます。

document.addEventListener("DOMContentLoaded", function() {
  var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));

  if ("IntersectionObserver" in window) {
    let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          entry.target.classList.add("visible");
          lazyBackgroundObserver.unobserve(entry.target);
        }
      });
    });

    lazyBackgrounds.forEach(function(lazyBackground) {
      lazyBackgroundObserver.observe(lazyBackground);
    });
  }
});

Largest Contentful Paint(LCP)への影響

遅延読み込みは、画像の読み込みを実際に必要になるまで遅延させることで、起動時の全体的なデータ使用量とネットワーク競合の両方を削減できる優れた最適化です。これにより、画像デコードに必要な時間が短縮され、起動時間が短縮され、メインスレッドでの処理が削減されます。

ただし、遅延読み込みは、ウェブサイトの Largest Contentful Paint LCP に悪影響を及ぼす可能性がある手法です。避けるべきことの一つは、起動時にビューポートにある画像の遅延読み込みを行うことです。

JavaScript ベースの遅延ローダーを使用する場合は、ビューポート内の画像の遅延読み込みを避ける必要があります。これらのソリューションでは、src 属性と srcset 属性のプレースホルダとして data-src 属性または data-srcset 属性を使用することが多いためです。ここでの問題は、ブラウザのプリロード スキャナが起動時に画像を検出できないため、画像の読み込みが遅れることです。

ブラウザレベルの遅延読み込みを使用してビューポート内の画像の遅延読み込みを行っても、バックファイアが発生する可能性があります。loading="lazy" がビューポート内の画像に適用されると、ブラウザがビューポート内にあると認識するまで画像の表示が遅れます。これはページの LCP に影響する可能性があります。

起動時にビューポートに表示される画像を遅延読み込みしない。このようなパターンは、サイトの LCP に悪影響を及ぼし、ユーザー エクスペリエンスにも悪影響を及ぼします。起動時に画像が必要な場合は、遅延読み込みを行わず、できるだけ速やかに読み込みます。

ライブラリの遅延読み込み

可能な限りブラウザレベルの遅延読み込みを使用してください。ただし、多くのユーザーが古いブラウザに依存しているなど、これがオプションにならない場合は、次のライブラリを使用して画像の遅延読み込みを行うことができます。

  • lazysizes は、画像や iframe を遅延読み込みする、フル機能の遅延読み込みライブラリです。使用するパターンは、ここに示すコードサンプルと非常によく似ており、<img> 要素の lazyload クラスに自動的にバインドされ、画像 URL を data-src 属性または data-srcset 属性、あるいはその両方で指定する必要があります。その内容は、それぞれ src 属性または srcset 属性にスワップされます。Intersection Observer(ポリフィル可能)を使用し、さまざまなプラグインで動画の遅延読み込みなどを行うように拡張できます。詳しくは、遅延サイズの使用方法をご覧ください。
  • vanilla-lazyload は、画像、背景画像、動画、iframe、スクリプトの遅延読み込みを行うための軽量なオプションです。Intersection Observer を活用し、レスポンシブ画像をサポートし、ブラウザレベルの遅延読み込みを可能にします。
  • lozad.js も、Intersection Observer のみを使用する軽量のオプションです。そのため、高性能ですが、古いブラウザで使用するには事前にポリフィルする必要があります。
  • React 固有の遅延読み込みライブラリが必要な場合は、react-lazyload を検討してください。Intersection Observer は使用しませんが、React でのアプリケーションの開発に慣れている人には、画像の遅延読み込みを行うことができます。