loading 属性と lazysizes の使い方(画像の遅延読み込み)
loading 属性の基本的な使い方や loading 属性がサポートされていないブラウザへの lazysizes を使った対応方法、及び lazysizes の基本的な使い方についての覚書です。
作成日:2021年3月2日
loading 属性
昨年(2020年)画像や iframe の遅延読み込みができる loading 属性(Lazy loading attributes)が標準仕様になったようです。
但し、現時点(2021年2月)では Safari は未対応で、Firefox は img 要素のみ対応しています。
can i use (Lazy loading via attribute for images & iframes)
使い方は img 要素や iframe 要素で loading 属性を使って以下の値を指定します。
値 | 意味 |
---|---|
eager | 遅延読み込みをしない(すぐにロード) |
lazy | 遅延読み込みをする(Lazy Load) |
Chrome などでは auto(遅延読み込みするかどうかをブラウザに委ねる)という値が利用可能なようですが、標準仕様には記載されていません。 将来的に変更される可能性があるため、標準仕様に含まれるまで使用しない方が良いと思われます。
<img loading="eager" src="/images/01.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/02.jpg" width="960" height="593" alt="">
以下は srcset 属性 が指定されている画像を遅延読み込みする場合の例です。
<img src="images/large.jpg" srcset="images/small.jpg 640w, images/medium.jpg 980w, images/large.jpg 1280w" sizes="100vw" alt="" loading="lazy">
picture 要素にも loading 属性を使った遅延読み込みが使えます。
ブラウザは source 要素のいずれかからロードする画像を決定しますが、loading 属性はフォールバックの img 要素に設定します。
<picture> <source media="(min-width: 980px)" srcset="images/large.png"> <source media="(min-width: 640px)" srcset="images/medium.png"> <img src="images/small.png" alt="" loading="lazy"> </picture>
width と height を設定
ブラウザが画像を読み込む際に、画像のサイズが明示的に指定されていないと、画像のサイズはすぐにはわからないためレイアウトのずれが発生する可能性があります。
ブラウザが画像用に十分なスペースをページ上に確保できるようにするには、全ての img タグに width 属性と height 属性の両方を含めるか、インラインスタイルでサイズを指定します。
最新のブラウザで画像の width 属性と height 属性を設定すると、ブラウザは本来のサイズを推測することもできるようです。
<img loading="lazy" src="/images/01.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/02.jpg" alt="" style="height:960px; width:593px;">
ファーストビューでは遅延読み込みしない
最初に表示されるビューポート(ファーストビュー)にある画像に対しては loading="lazy" を設定しないようにします。そして、ファーストビュー以降のスクロールしなければ見えない範囲に配置されている画像にのみ loading="lazy" を追加するようにします。
動作確認サンプル
以下は Chrome で loading="lazy" を指定した12枚の画像を入れたページを読み込んだ際の例で、最初の4枚のみが読み込まれています(機能確認のため全ての画像に loading="lazy" を設定しています)。
<body> <p id="message">このブラウザでは loading 属性が<span id="availability"></span></p> <img loading="lazy" src="/images/sample_01.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_02.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_03.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_04.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_05.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_06.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_07.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_08.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_09.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/sample_10.jpg" width="960" height="593" alt=""> <img loading="lazy" src="/images/large/sample_11.jpg" srcset="/images/small/sample_11.jpg 640w, /images/medium/sample_11.jpg 960w, /images/large/sample_11.jpg 1200w" sizes="100vw" alt=""> <picture> <source media="(min-width: 1200px)" srcset="/images/large/sample_12.jpg"> <img loading="lazy" src="/images/sample_12.jpg" alt=""> </picture> </body>
loading 属性の機能の検出
使用しているブラウザで loading 属性がサポートされているかどうかは JavaScript を使って検出することができます。
HTMLImageElement オブジェクト(interface)の prototype プロパティで画像(img)要素の操作に使用されるプロパティとメソッドが取得できます。
if ('loading' in HTMLImageElement.prototype) { // loading 属性をサポートしているブラウザの場合 } else { // loading 属性をサポートしていないブラウザの場合 }
loading 属性がサポートされていないブラウザへの対応
現時点では WebKit (Safari) などいくつかのブラウザでサポートされていませんが、Safari では「開発」メニューの「実験的な機能」で Lazy Image Loading を有効にすれば使えるので、それほど遠くない将来サポートされる可能性があるかと思います。
そのため、loading 属性を設定しておいてそれらのブラウザでサポートされるまで何もせずに待つというのも1つの対応かもしれません。
そうでなければ、JavaScript を使って loading 属性の機能がサポートされているかどうかを検出して、遅延読み込みのライブラリを使って対応することができます。
以下は loading 属性がサポートされていないブラウザでは lazysizes という遅延読み込みのライブラリを使って遅延読み込みを行う例で、Browser-level image lazy-loading for the web でも紹介されています。
遅延読み込みを行う img 要素に loading="lazy" と class="lazyload" を指定し、src 属性を data-src 属性に変更します。遅延読み込みのライブラリ lazysizes を使用する場合、img 要素に lazyload というクラスを指定し、画像の URL は data-src 属性に指定する必要があります。
関連項目:lazysizes の基本的な使い方
<!--遅延読み込みなし--> <img src="/images/sample_01.jpg" width="960" height="593" alt=""> <!--遅延読み込みする画像(見やすいように改行しています)--> <img loading="lazy" class="lazyload" data-src="/images/sample_02.jpg" width="960" height="593" alt="">
以下は srcset 属性を指定した img 要素と picture 要素の img 要素の例です。前述の img 要素同様、loading="lazy" と class="lazyload" を指定し、src 属性を data-src 属性に、srcset 属性を data-srcset 属性に変更します。
<img loading="lazy" class="lazyload" data-src="/images/large/sample_11.jpg" data-srcset="/images/small/sample_11.jpg 640w, /images/medium/sample_11.jpg 960w, /images/large/sample_11.jpg 1200w" sizes="100vw" alt=""> <picture> <source media="(min-width: 1200px)" data-srcset="/images/large/sample_12.jpg"> <img loading="lazy" class="lazyload" data-src="/images/sample_12.jpg" alt=""> </picture>
以下の JavaScript を </body> の直前に記述します。
loading 属性がサポートされている場合は、src 属性の値に data-src 属性の値を設定して通常の img 要素の HTML の属性に戻します。同様に img 要素の srcset 属性の値に data-srcset 属性の値を設定します。
loading 属性がサポートされていない場合は、動的に lazysizes ライブラリー(JavaScript)を読み込みます。以下では CDN の lazysizes を読み込むようにしていますが、lazysizes をダウンロードしてローカルに保存してあればそのファイルを読み込むこともできます。
<script> if ('loading' in HTMLImageElement.prototype) { //loading 属性がサポートされている場合 //loading 属性に lazy が指定されている img 要素 const images = document.querySelectorAll('img[loading="lazy"]'); images.forEach(img => { //img 要素の src 属性の値に data-src 属性の値を設定 img.src = img.dataset.src; }); //img 要素で srcset 属性を使っている場合(data-srcset が指定されている img 要素) const img_srcset = document.querySelectorAll("img[data-srcset]"); img_srcset.forEach(img_srcset => { //img 要素の srcset 属性の値に data-srcset 属性の値を設定 img_srcset.srcset = img_srcset.dataset.srcset; }); //picture 要素 const sources = document.querySelectorAll("source[data-srcset]"); sources.forEach(source => { //source 要素の srcset 属性の値に data-srcset 属性の値を設定 source.srcset = source.dataset.srcset; }); } else { //loading 属性がサポートされていない場合 // 動的に lazysizes ライブラリーを読み込む const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.0/lazysizes.min.js'; //script.src = 'path/to/lazysizes.min.js'; //ダウンロードしてある場合 document.body.appendChild(script); } </script> </body>
但し、上記の例の場合、img 要素の src 属性や srcset 属性を設定していないので、W3C のバリデーションでチェックすると「Error: Element img is missing required attribute src.」などのエラーが表示されます。
もし、このエラーが気になるようであれば、エラーが出ないようにするには以下のように HTML で img 要素の src 属性や srcset 属性にダミー画像を指定します。JavaScript の記述は同じで大丈夫です。
以下の例では base64 というデータ形式の 1×1px の gif 画像を src 属性や srcset 属性に指定して直接埋め込んでいるので、外部ファイルとしてのリクエストは発生しません。
<!--以下の画像は遅延読み込みなし--> <img src="/images/sample_01.jpg" width="960" height="593" alt=""> <!--以下は遅延読み込みする画像--> <img loading="lazy" class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="/images/sample_02.jpg" width="960" height="593" alt=""> <img loading="lazy" class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="/images/sample_03.jpg" width="960" height="593" alt=""> ・・・中略・・・ <img loading="lazy" class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="/images/large/sample_11.jpg" srcset="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 640w" data-srcset="/images/small/sample_11.jpg 640w, /images/medium/sample_11.jpg 960w, /images/large/sample_11.jpg 1200w" sizes="100vw" alt=""> <picture> <source media="(min-width: 1200px)" srcset="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-srcset="/images/large/sample_12.jpg"> <img loading="lazy" class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="/images/sample_12.jpg" alt=""> </picture>
前述の例の場合、loading 属性に lazy が指定されている img 要素には、全て data-src 属性及び lazyload クラスを設定することが前提になっています(data-src 属性が設定されていない要素では画像が表示されない)。
以下は img 要素に data-src 属性が設定されていれば img 要素の src 属性の値に data-src 属性の値を設定するように条件を追加した例です。
この場合、loading 属性に lazy が指定されている img 要素で data-src 属性及び lazyload クラスを設定していない場合は、loading 属性がサポートされているブラウザでのみ遅延読み込みされます。
//loading 属性がサポートされていれば if ('loading' in HTMLImageElement.prototype) { //loading 属性に lazy が指定されている img 要素 const images = document.querySelectorAll('img[loading="lazy"]'); //data-srcset が指定されている img 要素 const img_srcset = document.querySelectorAll("img[data-srcset]"); //data-srcset が指定されている source 要素 const sources = document.querySelectorAll("source[data-srcset]"); //loading="lazy" が設定されている要素があれば if(images.length > 0) { images.forEach(img => { //data-src 属性が設定されていれば img 要素の src 属性の値に data-src 属性の値を設定 if(img.dataset.src) { img.src = img.dataset.src; } }); } //data-srcset が指定されている img 要素があれば if(img_srcset.length > 0) { img_srcset.forEach(img_srcset => { //img 要素の srcset 属性の値に data-srcset 属性の値を設定 img_srcset.srcset = img_srcset.dataset.srcset; }); } //picture 要素 //data-srcset が指定されている source 要素があれば if(sources.length > 0){ sources.forEach(source => { //source 要素の srcset 属性の値に data-srcset 属性の値を設定 source.srcset = source.dataset.srcset; }); } } else { //loading 属性がサポートされていない場合は動的に lazysizes ライブラリーを読み込む // script 要素を生成 const script = document.createElement('script'); //ダウンロードしてある lazysizes を script 要素の src に指定 script.src = 'lazysizes.min.js'; //script 要素を body に追加 document.body.appendChild(script); }
lazysizes の基本的な使い方
lazysizes は画像を遅延読み込みするためのライブラリです。lazysizes の JavaScript ファイルを読み込んで遅延表示する img 要素にクラスを追加だけで画像の遅延読み込みができます。
picture 要素や srcset 属性、iframe 要素にも対応しています。また、必要に応じてカスタマイズできる拡張プラグイン(画像の縦横比の検出や背景画像、ビデオなどの遅延読み込み等)も多数用意されています。
lazysizes の読み込み
lazysizes の読み込みは以下の方法があります。
ファイルをダウンロード
lazysizes のサイトからファイルをダウンロードします。
lazysizes.min.js を適当な場所に保存します。
保存した lazysizes.min.js を読み込みます(パスは適宜保存した場所に合わせて変更します)。
<script src="lazysizes.min.js"></script>
CDN 経由で読み込む
CDN 経由で読み込むこともできます(cdnjs にリストがあります)。
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.0/lazysizes.min.js"></script>
npm でインストール
npm でインストールして import して読み込むこともできます。
npm install lazysizes --save
import 'lazysizes'; // 以下はプラグインをインポートする場合の例 import 'lazysizes/plugins/parent-fit/ls.parent-fit';
クラスの追加と属性の変更
遅延読み込みを適用したい画像(img 要素)の class に lazyload を設定(追加)し、src 属性の代わりに、data-src 属性で画像を読み込みます。
<img data-src="image.jpg" class="lazyload" alt="">
上記の場合、W3C のバリデーションでチェックすると「Error: Element img is missing required attribute src.」が表示されるので、このエラーが気になるようであれば以下のように src 属性にダミー画像を指定します。
<img data-src="image.jpg" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="lazyload" alt="">
基本的な使い方としては、JavaScript ファイルを読み込んで img 要素の data-src 属性で画像の URL を指定し、lazyload クラスを追加するだけで遅延読み込みが実現できます。
開発コンソールで画像要素を確認すると、読み込まれた要素の src 属性の値に data-src の値が設定され、クラス名が lazyload から lazyloaded に変わっていきます。
srcset 属性
img 要素や picture 要素(source 要素)の srcset 属性は data-srcset 属性に変更します。
また、lazysizes は画像の現在のサイズに対応する sizes 属性の自動設定をサポートしています。その場合、data-sizes の値を auto に設定します。
<img data-sizes="auto" data-src="image2.jpg" data-srcset="image1.jpg 300w, image2.jpg 600w, image3.jpg 900w" class="lazyload" />
マークアップのサンプル
以下のページにいろいろなパターンのコード(マークアップ)のサンプルが記載されています。
アニメーションを追加
画像を表示する際に CSS を使ってアニメーションを付けることもできます。
以下は遅延読み込みする画像(img 要素)に fade-in-bottom というアニメーション用のクラスを追加して、アニメーションを付けて表示する例です。
<img class="lazyload fade-in-bottom" data-src="/images/01.jpg" width="960" height="593" alt="">
opacity と transform 属性を使った transition のアニメーションを設定しています。画像が読み込まれた際に付与される lazyloaded というクラスにアニメーション実行後の状態を指定します。
.fade-in-bottom { opacity: 0; transform: translate(0, 100px); transition: 2s; } .fade-in-bottom.lazyloaded { transform: translate(0, 0); opacity: 1; }
srcset 属性を指定した imag 要素や picture 要素の img 要素にも同様にアニメーションを設定することができます。
<img class="lazyload fade-in-left" data-src="/images/large/11.jpg" data-srcset="/images/small/11.jpg 640w, /images/medium/11.jpg 960w, /images/large/11.jpg 1200w" data-sizes="auto" alt=""> <picture> <source media="(min-width: 1200px)" data-srcset="/images/large/12.jpg"> <img class="lazyload zoom-in" data-src="/images/12.jpg" alt=""> </picture>
.fade-in-left { opacity: 0; transform: translate(-100px, 0); transition: 2s; } .fade-in-left.lazyloaded { transform: translate(0, 0); opacity: 1; } .zoom-in { opacity: 0; transform: scale(0.7); transition: 3s; } .zoom-in.lazyloaded { transform: scale(1); opacity: 1; }
data-expand 属性
data-expand 属性を使うと、画像からの距離を指定して画像の読み込みを開始させることができます。
大きなサイズの画像を早めに読み込ませたり、画像の領域に入ってからアニメーションを開始させたりすることができます。
data-expand 属性には0以外の数値を指定できます。
- 正の値:読み込みを開始するスクロール位置が上になります(早く読み込まれます)
- 負の値:読み込みを開始するスクロール位置が下になります(遅く読み込まれます)
以下は画像がある領域に300px入ってから表示させる場合の例です。
<img class="lazyload fade-in-bottom" data-expand="-300" data-src="/images/01.jpg" width="960" height="593" alt="">
背景画像の遅延読み込み
ダウンロードしたファイルに含まれる unveilhooks プラグインを使うと、背景画像やビデオ、スタイルシート、 script 要素なども遅延読み込みすることができます。
ダウンロードした plugins 内の unveilhooks ディレクトリの ls.unveilhooks.min.js を読み込みます(または CDN 経由で読み込むこともできます)。
<!-- lazysizes の読み込み --> <script src="lazysizes.min.js"></script> <!-- unveilhooks プラグインの読み込み --> <script src="ls.unveilhooks.min.js"></script>
CDN を利用する場合の例
<!-- lazysizes の読み込み --> <script src="lazysizes.min.js"></script> <!-- CDN 経由での unveilhooks プラグインの読み込み --> <script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.0/plugins/unveilhooks/ls.unveilhooks.min.js"></script>
背景画像を表示する要素に lazyload クラスを指定し、data-bg 属性を追加して値に背景画像の URL を指定します。
<div class="bgimg lazyload" data-bg="/images/sample.jpg"></div>
CSS では background-image は指定しません。画像が読み込まれると、クラスが lazyload から lazyloaded に変わり、インラインスタイルで data-bg 属性に指定した画像が background-image で読み込まれます。
.bgimg { background-size: cover; background-repeat: no-repeat; background-position: center; height:600px; max-width: 960px; margin: 50px 0; }
以下は background-image にダミー画像(base64 形式の 1×1px の gif 画像)を指定する場合の例です(HTML 及び読み込む JavaScript は同じです)。
.bgimg { background-image: url("data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="); background-size: cover; background-repeat: no-repeat; background-position: center; height:600px; max-width: 960px; margin: 50px 0; }
クラスを介して背景画像を遅延読み込み
以下は data-bg 属性は使わずに、画像が読み込まれた際に変わる lazyloaded クラスを介して背景画像を遅延読み込みする例です。
<div class="bgimg lazyload"></div>
CSS では lazyloaded クラスに background-image を指定します(HTML に指定するクラスは lazyload ですが、CSS では遅延読み込み完了後のクラス .lazyloaded を指定します)。JavaScript の読み込みは前述の例と同じです。
.bgimg.lazyloaded { /* lazyloaded ※末尾に ed が付く */ background-image: url(/images/sample.jpg); background-size: cover; background-repeat: no-repeat; background-position: center; height:600px; max-width: 960px; margin: 50px 0; }
グラデーションとの併用
background-image に同時にグラデーションを指定する場合など、こちらの方法が便利かも知れません。
.bgimg.lazyloaded { background-image: linear-gradient(180deg, hsla(240,20%,20%,0.15), hsla(240,90%,5%,0.8)), url(/images/sample.jpg); background-size: cover; background-repeat: no-repeat; background-position: center; height:600px; max-width: 960px; margin: 50px 0; }
.bgimg { height:600px; max-width: 960px; margin: 50px 0; } .bgimg.lazyloaded { background-image: linear-gradient(180deg, hsla(240,20%,20%,0.15), hsla(240,90%,5%,0.8)), url(/images/sample.jpg); background-size: cover; background-repeat: no-repeat; background-position: center; }
以下のページに script 要素やスタイルシート、ビデオなどの遅延読み込みのサンプルが記載されています。
Intersection Observer バージョン
lazysizes は getBoundingClientRect() を利用していますが、Intersection Observer(IO)を利用したバージョンもあります。
can i use IntersectionObserver
但し、現時点では Intersection Observer のバージョンを標準にすることはなさそうで、ドキュメントなども用意されていません。
何故 Intersection Observer のバージョンを標準にしないかについては、以下のページで「Why IO is not the holy grail for lazysizes(IO が lazysizes の探求の対象ではない理由)」として説明されています。
IO を使ったからと言って必ずしもパフォーマンスがよくなるわけではなく、逆に問題もある(らしい)。
Intersection Observer で作成されたバージョンは src ディレクトリの中にある lazysizes-intersection.js というファイルになります。
lazysizes.min.js の代わりに、lazysizes-intersection.js を読み込めば試すことができます。
<script src="lazysizes-intersection.js"></script>