画像の遅延読み込み

画像は、<img> 要素として HTML にインラインで表示されるか、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 イベントで、このスクリプトはクラスが lazy のすべての <img> 要素の DOM を照会します。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 に悪影響を及ぼす手法です。避けたいことの 1 つは、起動時にビューポートにある画像を遅延読み込みすることです。

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

ブラウザレベルの遅延読み込みを使用してビューポート内の画像の遅延読み込みをしても、逆効果になることがあります。ビューポート内の画像に loading="lazy" を適用すると、画像がビューポートにあることをブラウザが認識するまで遅延し、ページの LCP に影響する可能性があります。

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

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

可能な限りブラウザレベルの遅延読み込みを使用することをおすすめしますが、それを使用できない状況(多くのユーザーが古いブラウザを使用している場合など)では、次のライブラリを使用して画像の遅延読み込みを行うことができます。

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