画像や動画の遅延読み込みには、測定可能なパフォーマンス上のメリットがありますが、慎重に行うべき作業ではありません。判断を誤ると 意図しない結果が生じる可能性がありますそのため、次の懸念事項に留意することが重要です。
スクロールせずに見える範囲に注意する
ページ上のすべてのメディア リソースを JavaScript で遅延読み込みしたくなるかもしれませんが、これは無理です。スクロールせずに見える範囲にあるものを遅延読み込みしないでください。このようなリソースは重要なアセットとみなされるため、通常どおり読み込む必要があります。
遅延読み込みは、スクリプトの読み込みが完了し実行を開始する DOM がインタラクティブになるまで、リソースの読み込みを遅らせます。スクロールしなければ見えない範囲の画像の場合は問題ありませんが、できる限り早く表示されるように、スクロールせずに見える範囲の重要なリソースは標準の <img>
要素で読み込む必要があります。
もちろん、最近はウェブサイトがさまざまなサイズの画面で表示されるため、スクロールせずに見える範囲がはっきりしません。ノートパソコンのスクロールせずに見える範囲が、モバイル デバイスではスクロールしなければ見えない範囲にある場合もあります。あらゆる状況でこれに対処するための確実なアドバイスはありません。ページの重要なアセットのインベントリを作成し、それらの画像を通常の方法で読み込む必要があります。
また、折りたたみ線については、遅延読み込みをトリガーするためのしきい値ほど厳格にする必要はありません。スクロールしなければ見えない位置にある程度の距離のバッファゾーンを設定して、ユーザーがビューポートにスクロールするかなり前に画像の読み込みを開始する方が、目的によってはより適切な場合があります。たとえば、Intersection Observer API を使用すると、新しい IntersectionObserver
インスタンスを作成するときに、オプション オブジェクトに rootMargin
プロパティを指定できます。これにより、要素に実質的にバッファが付与され、要素がビューポートに入る前に遅延読み込み動作がトリガーされます。
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
// lazy-loading image code goes here
}, {
rootMargin: "0px 0px 256px 0px"
});
rootMargin
の値が CSS の margin
プロパティに指定した値と似ている場合、それは同じです。この場合、観測される要素(デフォルトではブラウザのビューポートですが、root
プロパティを使用して特定の要素に変更できます)の下マージンが 256 ピクセル拡大されます。つまり、画像要素がビューポートの 256 ピクセル以内にあるときにコールバック関数が実行され、ユーザーが実際に目にする前に画像の読み込みが開始されます。
Intersection Observe をサポートしていないブラウザで同じ効果を実現するには、スクロール イベント処理コードを使用して、getBoundingClientRect
チェックにバッファを含めます。
レイアウト シフトとプレースホルダ
プレースホルダを使用していない場合、メディアの遅延読み込みによってレイアウトがシフトする可能性があります。このような変更は、ユーザーが方向感覚を失い、システム リソースを消費してジャンクを引き起こす高コストの DOM レイアウト操作を引き起こす可能性があります。少なくとも、ターゲット画像と同じサイズを占有する単色のプレースホルダを使用するか、読み込み前にメディア アイテムのコンテンツを示す LQIP や SQIP などの手法を使用することを検討してください。
<img>
タグの場合、この属性が最終ページ URL で更新されるまで、src
は最初にプレースホルダを指している必要があります。<video>
要素で poster
属性を使用して、プレースホルダ画像を指定します。また、<img>
タグと <video>
タグの両方で、width
属性と height
属性を使用します。これにより、プレースホルダから最終画像に移行しても、メディアの読み込み時に要素のレンダリング サイズが変更されることはありません。
画像デコードの遅延
JavaScript で大きな画像を読み込んで DOM にドロップすると、メインスレッドがロックされて、デコード中にユーザー インターフェースが短時間応答しなくなることがあります。画像を DOM に挿入する前に decode
メソッドを使って非同期にデコードすると、この種のジャンクを減らすことができます。ただし、この機能はまだどこでも利用できるわけではなく、遅延読み込みロジックが複雑になるので注意してください。使用する場合は、確認する必要があります。以下に、フォールバックで Image.decode()
を使用する方法を示します。
var newImage = new Image();
newImage.src = "my-awesome-image.jpg";
if ("decode" in newImage) {
// Fancy decoding logic
newImage.decode().then(function() {
imageContainer.appendChild(newImage);
});
} else {
// Regular image load
imageContainer.appendChild(newImage);
}
こちらの CodePen リンクで、この例に似たコードの動作を確認してください。大部分の画像がかなり小さい場合、大きな効果は期待できませんが、大きな画像の遅延読み込みと DOM への挿入の際に確実にジャンクを減らすことができます。
データが読み込まれない場合
なんらかの理由でメディア リソースの読み込みが失敗し、エラーが発生することがあります。発生する可能性があるのはどのような場合ですか?状況に応じて異なりますが、ここでは、HTML キャッシュ ポリシーが短時間(5 分間など)で、ユーザーがサイトにアクセスした場合、またはユーザーが古いタブを長時間(数時間など)開いたままで戻ってきてコンテンツを閲覧した場合を考えます。このプロセスのどこかの時点で、再デプロイが行われます。このデプロイでは、ハッシュベースのバージョニングによってイメージ リソースの名前が変更されるか、イメージ リソースの名前が完全に削除されます。ユーザーが画像の遅延読み込みを行う頃には、リソースは利用できなくなり、失敗します。
このような状況は比較的まれですが、遅延読み込みが失敗した場合のバックアップ計画を立てたほうがよい場合があります。画像の場合、次のようなソリューションになります。
var newImage = new Image();
newImage.src = "my-awesome-image.jpg";
newImage.onerror = function(){
// Decide what to do on error
};
newImage.onload = function(){
// Load the image
};
エラー発生時の対処方法はアプリケーションによって異なります。たとえば、画像のプレースホルダ領域をボタンに置き換えて、ユーザーが画像を再度読み込めるようにしたり、画像のプレースホルダ領域にエラー メッセージを表示したりできます。
他のシナリオも考えられます。何をするにしても、エラーが発生したときにユーザーに通知し、問題発生時にユーザーがとるべきアクションを示しておくのは良いことです。
JavaScript の可用性
JavaScript が常に利用できるとは限りません。画像を遅延読み込みする場合は、JavaScript が利用できない場合に画像を表示する <noscript>
マークアップを指定することを検討してください。最もシンプルなフォールバックの例は、JavaScript がオフになっている場合に <noscript>
要素を使用して画像を表示する方法です。
JavaScript がオフになっている場合、ユーザーにはプレースホルダ画像と <noscript>
要素に含まれる画像の両方が表示されます。これを回避するには、次のように no-js
のクラスを <html>
タグに配置します。
<html class="no-js">
次に、<link>
タグを介してスタイルシートがリクエストされる前に、<head>
にインライン スクリプトを 1 行挿入します。JavaScript がオンになっている場合は、<html>
要素から no-js
クラスが削除されます。
<script>document.documentElement.classList.remove("no-js");</script>
最後に、JavaScript を使用できないときに Lazy クラスで要素を非表示にする CSS を使用します。
.no-js .lazy {
display: none;
}
これにより、プレースホルダ画像の読み込みが妨げられることはありませんが、より望ましい結果が得られます。JavaScript がオフになっているユーザーには、プレースホルダ画像以上のものが渡されます。これはプレースホルダよりも優れており、意味のある画像コンテンツがまったくありません。