スキップしてメイン コンテンツに移動

サイトに画像の遅延読み込みをGoogle推奨のIntersection Observerで実装しよう

ブログの表示速度をPageSpeed Insightsで調査すると改善できる項目として「オフスクリーン画像の遅延読み込み」が出て来て取り入れると0.1秒くらい短縮できると分かる。

オフスクリーンの非表示の画像は、クリティカルなリソースをすべて読み込んだ後に遅れて読み込むようにして、インタラクティブになるまでの時間を短縮することをご検討ください。

改善できる項目 via PageSpeed Insights

真っ先に心配なのはSEO(検索エンジン最適化)対策で、専らGoogle検索でのサイト評価が下がるとしたら辛い。必要な画像が的確に表示されないとサイトを巡回するクローラーに読み込まれず、コンテンツの品質が低いと判断されて検索結果で不利になってしまうのではないか。個人的に検索エンジンからのアクセスダウンは何としても避けなくてはならないと感じる。

サイトの表示速度もGoogle検索でのサイト評価の要因なので、可能なかぎりは是非とも取り入れておきたい画像の遅延読み込みでもある。

昨年、パソコンと同様にスマホでもサイトの表示速度がサイト評価に重視されるスピードアップデート(ページの読み込み速度をモバイル検索のランキング要素に使用します)が導入されてそれ自体は物凄く遅いサイトでなければ不利にはならない検索アルゴリズムらしいけど、とにかく訪問者の利便性が低い状態が有利ではないはずだから少しでもサイトの表示速度を上げる取り組みはSEO対策で欠かせない。

Google推奨でSEO対策にも向くIntersection Observerの利点

PageSpeed InsightsはGoogleが提供しているWebサービスだからサイトの表示速度を高めるための様々なアドバイスはSEO対策を踏まえて行われているかも知れない。

気になる「オフスクリーン画像の遅延読み込み」を改善できる項目のLearn more(もっと学ぶ)を確認するとOffscreen Images(オフスクリーン画像)でJavaScriptのIntersection Observer APIを使ったサイト作成が推奨されていた。

オフスクリーン画像の遅延読み込みにはIntersection Observerの使用を賢い決断と考えて下さい。例えばとても長いページの下段に画像があると推測されるような場合です。Intersection Observerによって利用者はスクロールで下がる正に途中から画像を読み込めるのです。

Offscreen Images via Tool for Web Developers(訳出)

なぜかの詳細はさらにIntersectionObserver's Coming into View(Intersection Observerが視野に入れる)で、示されている。

従来、JavaScriptでもスクロールイベントの引き上げか周期タイマーを使うか、あるいはgetBoundingClientRect()を呼んで座標系(要素の寸法やビューポートへの位置取り)から処理していて何れもブラウザがページを再配置するために無駄を出してしまって画像の遅延読み込みを改善する方法にせよ、結果は悲劇的に遅かったといわれる。

この可視性テストのもっと効果的な遂行がIntersection Observerの企図された目的で、Chrome 51(この記事の時点ではβリリースです)が開始しました。Intersection Observerはいつ監視された要素がブラウザのビューポートへ出入りするかを教えてくれます。

IntersectionObserver's Coming into View via Google Developer(訳出)

JavaScriptのIntersection Observerは新しい。Chrome51で開始されたのはCan I use…によると2016年5月25日だから三年近く経っているにせよ、従来の方法で避けられなかったブラウザの動作上の無駄を削ぎ落としてそれこそ画像の遅延読み込みに特化したプログラムを単純明快にも実現しているわけだった。聞くやサイト作成に取り入れたいと願う。

ただし検索エンジンのクローラーが一時的でも表示されない像を読み取ってサイト評価に好影響を与えるかどうかは良く分からないので、さらに調べてみるとGoogleが公式の発言を行って画像の遅延読み込みにJavaScriptのIntersection Observerを使用するサイト作成を推奨する情報を掴んだから大分と安心できた。

サイトのページ上のコンテンツがすべて確実に Googlebot で認識されるようにするには、遅延読み込みを実装する際、IntersectionObserver API と Polyfill を使うことにより、どの関連コンテンツも、ビューポートに表示できる場合に必ず読み込まれるようにします。

画像の遅延読み込みはサイトの表示速度を高めるから便利で良いと思うし、アクセスアップに繋がるはずなのに検索エンジンに把握できないためにサイト評価を上げられず、SEO対策に不利では反対にアクセスダウンに陥り兼ねないのでは手が出せない。

Googleが推奨するかぎり、もはや心置きなく取り入れるかぎりだ。

追記:2019年5月からGoogleのクローラーがChromeブラウザと同等のレンダリングエンジンを搭載してサイトのIntersection Observerの読み取りが可能になった(Googlebot が常に最新のレンダリング エンジンをサポートするようになります)からサイト評価への好影響を明らかに期待できる。

JavaScriptのIntersection Observerでの画像の遅延読み込みの設置法

intersectionRatio [CC BY 3.0], via Google Developers

ブラウザのビューポートへの要素の出入り、すなわちデバイスの画面の中にコンテンツがあるかないかを判定するために使われるのがIntersection Observer APIで、何等かの仕方でJavaScriptのプログラムに組み込めばサイトのまだ表示されない画像を初めて表示される時点から普通よりも遅延して読み込ませるようにできる。

IntersectionObserverのプログラムのアウトライン

var io = new IntersectionObserver(
  entries => {
    console.log(entries);
  },
  {
    /* デフォルトのオプションを使用。詳細は以下 */
  }
);
// 要素の監視を開始する
io.observe(element);

// 要素の監視を停止する
// io.unobserve(element);

// IntersectionObserverを完全に停止する
// io.disconnect();
IntersectionObserver's Coming into View via Google Developers(訳出)

オプションが記載されないデフォルトの場合の要素はブラウザのビューポートと少しでも交差するときと完全に離れるときにコールバックを行う。

もしも必要ならば複数の要素でもobserve()で呼んで同一のIntersectionObserverのインスタンスに何回でも使い回せるだろう。

コールバックに渡されるentriesの項目はIntersectionObserverEntityのオブジェクトを配列で有する。

IntersectionObserverEntityの七つのプロパティ

var calledback = function(entries, observer) {
  entries.forEach(entry => {
    // 各entryは監視された一つの交差の変化を示す
    // 対象となる要素:
    //  entry.boundingClientRect
    //  entry.intersectionRatio
    //  entry.intersectionRect
    //  entry.isIntersecting
    //  entry.rootBounds
    //  entry.target
    //  entry.time
  });
};
Intersection Observer API via MDN(訳出)

IntersectionObserverEntityはIntersectionObserverのコールバックへの入力かIntersectionObserver.takeRecords()を呼び出すかで得られる。

Intersection Observerは交差の変化を非同期に捉えるけれどもコールバックはメインスレッドで為される。なるべ早く動作する必要があって時間を要する処理が求められるならばrequestIdleCallbackメソッドを使うと良いらしい。コールバックの呼び出しの優先度が下がり、ブラウザの待機時間に処理が行われるようになる。

boundingClientRect
監視された要素の矩形/getBoundClientRect()による寸法やビューポートへの位置取り
intersectionRatio
可視化された要素の内部的な割合
intersectionRect
rootと要素の二つの矩形が交差したところの矩形
isIntersecting
可視化された要素があるかないかの真偽値
rootBounds
rootの矩形/初期値はブラウザのビューポート自体のgetBoundClientRect()による寸法やビューポートへの位置取り
target
rootと交差して可視化する要素を指す
time
ファイルの読み込みから交差するまでのDOMHighResTimeStamp型の時間

Intersection Observerの三つのオプション

JavaScriptのIntersection Observerにはオプションが三つ付いていて固有の範囲と対象が交差する判定の仕方を調節できる。

root: null/例_document.query.selector("name")
判定の領域を指定する:サイトに別窓のコンテンツをスクロール付きで配置するような場合に役立つ/デフォルトはブラウザのビューポートだ。
rootMargin: "0px"/例_"0 0 64px 0"
判定の範囲を指定する:要素の上下左右を交差するrootの端よりも大小とずらして発動させられる/デフォルトは「0px」だ。単位はpxか%を使って常に省略しない。%はrootの割合なので、オプションからrootを指定して親ボックスの大きさが確定しない場合は動作しない。値の入力の仕方は一つで上下左右、二つで上下と右左、三つで上下と右と左、四つで上と右と下と左になる。
threshold: 0/例_[0, 0.5, 1.0]
判定の割合を指定する:要素がrootの端とどのくらい交差したら発動するかを0から1まで分割して決められる/デフォルトは「0」だ。値は要素の内部的な割合で、最大の1で全体が閾値(rootと交差済みの状態)として扱われる。

※全体は半角波括弧({})に入れて一つ以上を設定する際は半角コンマ(,)で区切って記載する。

スクロールで画像を赤から青へ切り替えるサンプル

半分以上が見えると赤から青に変わる

JavaScriptのIntersection Offscreenによる赤と青の切り替え用の画像

半分以上が見えると赤から青に変わる

HTML

<div id="ioa">
<p>半分以上が見えると赤から青に変わる</p>
<img class="iom" src="画像URL(赤)" data-src="画像URL(青)">
<p>半分以上が見えると赤から青に変わる</p>
</div>

CSS

#ioa {text-align:center;height:240px;width:auto;overflow:scroll;border:2px #c0c0c0 solid;}
#ioa p {margin:70px 0;}
.iom {min-width:320px;min-height:320px;}

JavaScript

var elm = document.querySelector("img.iom");
var dim = elm.src;
var io = new IntersectionObserver(
    entries => {entries.forEach(function(entry) {
    if (entry.isIntersecting) {
      entry.target.src = entry.target.dataset.src;
    } else {
      entry.target.src = dim;
    }});
  },
  {
  root: document.querySelector("div#ioa"),
  rootMargin: "0px",
  threshold: 0.5
  }
);
io.observe(elm);

サイトの可視化を監視したいコンテンツをdocument.querySelectorメソッドなどで取り込んでおいてobserveメソッドの引数としてIntersectionObserverへ放り込んでインスタンスを与えると仕込まれた任意のプログラムの実行結果が一つの戻値として得られる。

サイトの画像の遅延読み込みに無料で使える二つのプログラム

JavascriptのIntersection Observerの方法でサイトに画像の遅延読み込みを実装するには監視する要素についてHTMLの画像のimgタグの画像URLを記載するsrc属性を予め空にしておいて通常はブラウザに表示されないdata-src属性に代わりに記載しておいたのを後から移動させるようにするのが一般的な方法なんだ。

entry.target.src = entry.target.dataset.src;

JavaScriptでなくては画像が表示されなくなってしまうので、どんな場合でも表示するためにはHTMLのnoscriptタグで代替画像も一緒に載せるべきだ。

サイトで画像の遅延読み込みを取り入れるにはもっとパフォーマンスを高めたり、他の便利な機能を付けたりできるのが良いと思う。巷に無料で使える優れたプログラムが提供されているので、簡単な使い方を含めて紹介しておきたい。

Jeremy Wagnerのスクリプト(Google Developers)

簡潔なソースコードで、しかも要素の取得や監視の切り替えやフォールバック(Intersection Observer APIが使えないブラウザのための設定が可能だ)など、サイトで画像の遅延読み込みを的確に行えるプログラムなので、非常に洗練されているというか、気に入って僕も使っている。

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 a more compatible method here
  }
});

プログラムはApache 2.0 Licenseで提供されていて著作権はJeremy Wagnerにあるけれどもこの場合は作者名とライセンスの表記と共にどんなサイトでも改変可能で無料で使用して構わない。

/* Copyright Jeremy Wagner 2019 | Apache 2.0 License | https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video/ */

他人がホームページやブログに取り入れる場合はソースコードの冒頭に作者名とライセンスのコメントを付けておくのが適法だ。

使い方は遅延読み込みを求める画像のimgタグに「.lazy」のclassを付ける。そしてimgタグの画像URLのsrc属性をdata-src属性、あればsrcset属性もdata-srcset属性へ書き換える。さらにスクリプトのソースコードの全体をscriptタグの<script></script>で囲ってサイトのbodyの閉じタグの</body>の上の近くなどに記載する。すると当該の画像の遅延読み込みがプログラムに従って動作する。

カスタマイズはIntersection Observerのオプションが使える。

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // Lazy loading image code goes here
}, {
  rootMargin:"0px 0px 256px 0px"
});
イメージと動画の遅延読み込み via Web Fundamentals

全体のスクリプトでは終わり頃の「lazyImages.forEach」の前の「);」の前がIntersection Observerのオプションのスペースになっている。直前に新しく半角コンマ(,)を打たなくてはからないからプログラムを挿入するだけでは上手く行かない。スペースの前の「}」の次に半角コンマを打って区切ってからオプションのプログラムを配置すると大丈夫だ。

他にフォールバックを設定できるのが有り難い。現在は大半のブラウザが対応済みとはいえ、もしもIntersection Observerが新しくてまだ動作しなければ画像が表示されないので、代替用のプログラムを記載しておくのが望ましい。フォールバックは一行コメントの「// Possibly fall back to a more compatible method here」(あるいはより互換的な手法へのフォールバックはここ)の部分に記載する。必要なければelse { // Possibly fall back to a more compatible method here }を削除して構わないけれども波括弧にプログラムを配置する。

lazyImages.forEach(function(lazyImage) {
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;});

JavaScriptで画像URLを非表示のdata付きの属性から表示のdataなしの属性へ切り替えてサイトのソースコードを画像の遅延読み込みを取り入れない状態へ戻している。

公開元のGoogle Developersのイメージと動画の遅延読み込みにはIntersection Observer APIが使えないブラウザとの互換性や背景画像や動画の遅延読み込みやプレースホルダー画像(非表示用)の使い方やJavaScriptがない場合の対処法なども取り上げられている。

lozad.jsApoorv Saxenaのライブラリー(GitHub)

JavaScriptだけの軽量なライブラリーで――バージョン1.9.0は2.33KB/gzip圧縮で1.04KBしかない――Intersection Observer APIを使ったスムーズな画像の遅延読み込みを実現している。

lazad.jsのロゴバナー

GitHubで公開されていてライブラリーはサイト作成がNode.jsならばlazad -npmでインストールするか、その他はlazy.jslazad CDN by jsDelivrでscriptタグを貰ってHTMLのheadの閉じタグのの上の近くなどに記載する。

プログラムはThe MIT Licenseで提供されていて著作権はApoorv SaxenaJeremy Wagnerにあるけれどもこの場合は作者名とライセンスの表記と共にどんなサイトでも改変可能で無料で使用してかまわない。

loyad.jsはライブラリーに全て記載されているので、サイトに取り入れながら特に何もする必要はないと思う。

使い方は遅延読み込みを求める画像のimgタグに「.lozad」のclassを付ける。そしてimgタグの画像URLのsrc属性をdata-src属性、あればsrcset属性もdata-srcset属性へ書き換える。さらに「.lozad」のインスタンス化のためのプログラムをscriptタグの<script></script>で囲ってサイトのbodyの閉じタグのの上の近くなどに記載する。

デフォルトのソースコード

const observer = lozad();
observer.observe();

DOMの要素を参照する場合

const el = document.querySelector('img');
const observer = lozad(el);
observer.observe();

※HTMLの「img」で監視対象のインスタンス化を行う。他のDOMの要素の「div」などへの書き換えや追加が可能になる。

オプション付きのバージョン

const observer = lozad('.lozad', {
    rootMargin: '0px',
    threshold: 0
});
observer.observe();

三つの仕方のどれかでobserveメソッドを発動すると当該の画像の遅延読み込みがプログラムに従って動作する。

カスタマイズはlozad.jskに必須の要素のインスタンス化のスクリプトの例えばlozad()の部分などに他のプログラムを追加して行う。

交差中の関数定義の変更

const observer = lozad('.lozad', {
    load: function(el) {
        console.log('loading element');
        el.src = el.getAttribute('data-src');
    }
});
observer.observe();

※デフォルトの画像の切り替えも解除される。

監視する要素を拡張する

const observer = lozad('.lozad', {
    loaded: function(el) {
         el.classList.add('loaded');
    }
});
observer.observe();

※デフォルトの画像の切り替えは解除されない。

先読みの画像を指定する

const observer = lozad();
observer.observe();
const coolImage = document.querySelector('.image-to-load-first');
observer.triggerLoad(coolImage);

※表示されなくてもブラウザが読み込んで交差したら直ぐに表示できるようにする。HTMLのimgタグに「.image-to-load-first」のclassが付いた画像一枚が対象になる。

公開元のGitHubのApoorvSaxena/lozad.jsには画像全般のpictureタグや他のページから取り込むiframeタグでのコンテンツの遅延読み込みなども取り上げられている。

参考:IntersectionObserver これなら簡単で便利!要素がビューポートに表示されているかを判定できる -Intersection Observer IntersectionObserverで要素が画面内に入ってきたかを判定する Intersection Observerで要素が画面内に入ったときにアニメーションや自動スクロールをスタートする Intersection Observer を用いた要素出現検出の最適化

コメント

些細な日常の人気の投稿

PlayストアでAndroidアプリのダウンロードが非常に遅い場合の打開策

イメージ

早川愛の高校野球の夏の甲子園の大会歌の栄冠は君に輝くの独唱のソプラノの美声

イメージ

ジャパネットたかたの丸尾詩織の商品説明に気持ちが入っていて素晴らしい理由

イメージ