はじめに
画像の遅延読み込みを自前で実装しようとすると、意外と面倒なことに気づきます。IntersectionObserverを使うにしても、スクロールコンテナのケース、動的に追加された要素のケース、レスポンシブ画像のsrcset計算まで含めると、あっという間にコード量が膨らんでしまいます。
lazysizesは、この面倒な部分をまるごと引き受けてくれるJavaScriptライブラリです。HTMLにclass="lazyload"とdata-srcを書くだけで、あとはライブラリが自動的に検知して読み込んでくれます。JS側の初期化コードは基本的に不要です。
とはいえ、読むより触った方が早いと思います。スクロールに応じて画像が読み込まれる様子をその場で確認できるので、先に挙動を見たい方はこちらからどうぞ。
lazysizesとは
lazysizesは、画像・picture/srcsetによるレスポンシブ画像・iframe・スクリプト/ウィジェットなどを対象にした、SEOフレンドリーな遅延読み込みライブラリです。GitHub上で17,000以上のスターを獲得しており、長年にわたって多くのサイトで使われ続けています。
主な特徴
- 設定不要(自己初期化) -
class="lazyload"を付けるだけで動作し、JS側の初期化コードを書く必要がありません - 動的要素の自動検知 - あとからDOMに追加した画像も自動的に検知して遅延読み込みの対象になります
- SEOフレンドリー - Googleのクローラーがスクロールできないユーザーエージェントだと判断すると、すべての画像を即座に表示します
- レスポンシブ画像対応 -
data-sizes="auto"を指定するだけで、表示幅に応じたsizes属性を自動計算します - 軽量かつ高性能 - フレームワークに依存せず、ビューポート内・ビューポート近傍の要素を優先的に読み込むことでjank(カクつき)を抑えます
インストール
npmでインストールする場合:
npm install lazysizes --save
CDN経由で直接読み込む場合:
<script src="https://cdn.jsdelivr.net/npm/lazysizes/lazysizes.min.js" async></script>
lazysizesのサンプルを動かす
下のサンプルは、lazysizesをimportしただけの状態で、class="lazyload"とdata-src属性を持つimg要素をスクロールコンテナ内に配置したものです。lazysizesのlazyloadedイベントを使って、実際に画像が読み込まれたタイミングをコンソールに出力しています。
まずは要点だけを抜き出したコードがこちらです。
import 'lazysizes@5.3.2'
// class="lazyload" data-src="..." の要素は
// 何もしなくても自動的に検知・読み込みされる
document.addEventListener('lazyloaded', (e) => {
console.log('読み込み完了:', e.target.currentSrc || e.target.src)
})
実際に動かせるものが下です。灰色の枠内を下にスクロールすると、画像が読み込まれてコンソールにログが出力されます。
ポイントは、import 'lazysizes'しているだけでスクロールを監視するコードを一切書いていないことです。data-srcをdata-srcsetに変えたり、画像の枚数を増やしたりしても、コード側の変更は不要です。
基本的な使い方
lazysizesの基本的な使い方は、img要素にclass="lazyload"を付けて、srcの代わりにdata-srcを使うだけです。
<script src="lazysizes.min.js" async></script>
<!-- 通常の画像 -->
<img data-src="image.jpg" class="lazyload" alt="通常の画像" />
<!-- レティナ対応 -->
<img data-srcset="image1x.jpg 1x, image2x.jpg 2x" class="lazyload" alt="レティナ対応" />
<!-- iframe(YouTube埋め込みなど) -->
<iframe
class="lazyload"
data-src="https://www.youtube.com/embed/xxxxxxxxxxx"
frameborder="0"
allowfullscreen
></iframe>
npm経由で使う場合はビルドツールに組み込みます。
import 'lazysizes'
これだけで、ビューポートに入った要素から順番にdata-srcの値がsrcにコピーされ、画像が読み込まれていきます。
実践的なユースケース
レスポンシブ画像のsizes自動計算
srcsetを使ったレスポンシブ画像では、本来sizes属性を手動で計算・記述する必要がありますが、これが地味に面倒です。lazysizesはdata-sizes="auto"を指定するだけで、画像の表示幅からsizesを自動計算してくれます。
<img
data-sizes="auto"
data-srcset="image-300.jpg 300w,
image-600.jpg 600w,
image-900.jpg 900w"
class="lazyload"
/>
下のサンプルでは、data-srcsetに3種類の幅の画像を渡し、lazyloadedイベント発火時に選択された画像URLと自動計算されたsizesをコンソールに出力しています。画面幅を変えてLiveCodesを再実行すると、選ばれる画像が変わることを確認できます。
data-srcsetの幅記述子(300wなど)を増やすほど、より細かい段階で最適な画像が選ばれるようになります。
動的に追加した要素も自動検知する
SPAやAjaxで後から画像を差し込むケースでは、多くの遅延読み込みライブラリで「再初期化」の呼び出しが必要になります。lazysizesはMutationObserverベースで動作しているため、あとからDOMに追加したclass="lazyload"要素も、追加のコードなしで自動検知します。
// ボタンで動的に画像を追加しても
// lazysizes側で再初期化コードは一切不要
const img = document.createElement('img')
img.className = 'lazyload'
img.dataset.src = 'new-image.jpg'
document.body.appendChild(img)
下のサンプルでは、ボタンを押すたびにclass="lazyload"を持つimg要素をappendChildで追加しています。フェードインは.lazyloading/.lazyloadedクラスへのCSSトランジションで実現しています。
「画像を追加」ボタンを何度か押してみてください。追加したそばからlazyloadedイベントが発火し、フェードインしながら表示されるのが確認できます。unveilやrefreshのような再初期化APIを呼んでいない点がポイントです。
まとめ
lazysizesは、画像・iframeの遅延読み込みを「設定不要・自己初期化」という思想でシンプルに解決してくれるライブラリです。
class="lazyload"とdata-srcを書くだけで動作を開始するdata-sizes="auto"でレスポンシブ画像のsizes計算まで自動化できる- あとから追加した要素も自動検知するため、SPAやAjaxとの相性が良い
自前でIntersectionObserverとMutationObserverを組み合わせて実装する前に、まずlazysizesを試してみる価値は十分にあると思います。
