Bloggerの投稿画像の自動的な遅延読み込みを確実に行うためのプログラム

自分の写真結城永人 -

Bloggerの投稿画像をHTMLのloading-lazyで自動化するプログラムを紹介したけれども動作を詳しく調べた結果、AndroidのChromeで初回画面から2000pxくらい下の画面外の画像しか遅延読み込みが発動しなかった。さらにYouTube動画を止めることは全くできなかった。ブラウザの対応状況はどれも同じではないと聞くけれどもたぶん大きな違いはないかも知れない。

何れにしてもHTMLのloading-lazyによるブログの表示速度の高速化は非常に限定的だと分かったので、もはや元々のJavaScriptのIntersection Observer APIを再び使って画面外の画像や動画を確実に止めてパフォーマンスをきっちり上げるためのプログラムへの書き換えを追加したくなった。

PageSpeed Insightsの合格した監査の「オフスクリーン画像の遅延読み込み」の項目
合格した監査|PageSpeed Insights

PageSpeed Insightsでチェックしても「オフスクリーン画像の遅延読み込み」に合格するからGoogle検索などの検索エンジンも把握してサイト評価を高めてブログのアクセスアップに繋がるかも知れない。

ブログへの取り付け方はloading-lazyのものと同じなので、本稿ではJavaScriptの本体のプログラムだけを掲載する。

使う人は制作者の結城永人の著作権の表記もソースコードに併せて載せておく(MITライセンスでカスタマイズも再配布も許可する)から消さないで欲しい。

僕が提供している非公式テーマのImaginaryを使っている場合は著作権の表記が含まれているから今回の分を繰り返す必要はない。

Intersection Observerのバージョン

画像だけを遅延読み込みするソースコード

初回画像に入らない画像の読み込みを止める。

サンプルでは判定を、240px、増やしたから厳密には初回画像から、2401px、下の見えない画像は即座に読み込まれる。画面に入った瞬間から画像が読み込まれるとそこから時間がかかって長く待たされる可能性がある。だから画面に入る瞬間よりも少し早めに読み込みを開始する方がスムーズに表示できるので、判定の余白を取る。

JavaScript

/* Copyright: Nagahito Yuki 2022 | | License: The MIT License */
const psbd = Blog1.querySelector("div.post-body"), blct = psbd.querySelector("noscript.blog-content"), npsbd = psbd.cloneNode(false);
npsbd.insertAdjacentHTML("afterbegin", blct.innerText);
const blimgs = npsbd.querySelectorAll("img");
if (blimgs) {
Promise.all([...blimgs].map(blimg => {
const aroi = blimg.dataset.originalWidth / blimg.dataset.originalHeight;
if (blimg.height) {
if (!blimg.width && aroi) blimg.width = blimg.height * aroi; } else if (blimg.width) {
if (aroi) blimg.height = blimg.width / aroi; } else if (aroi && !blimg.style.aspectRatio) {
blimg.style.aspectRatio = aroi; } 
blimg.dataset.src = blimg.src; blimg.src = "";
return blimg; })).then(results => {
psbd.parentNode.replaceChild(npsbd, psbd);
const postContentObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const et = entry.target;
et.src = et.dataset.src; delete et.dataset.src; postContentObserver.unobserve(et); }}); }, {
rootMargin: "240px" });
results.forEach(result => {
if ("IntersectionObserver" in window) postContentObserver.observe(result); else result.src = result.dataset.src; }); }); } else {
psbd.parentNode.replaceChild(npsbd, psbd); }

※冒頭のコメント(/* ... */)はソースコードの著作権の表記。

初回画面の下から240px以内に入った画像だけが読み込まれる。

プログラムの「rootMargin: "240px" 」の数値が判定の余白になっているので、好みで調節することができる。縮めると画像が画面に入る近くで、読み込まれる。0ならば画面に入る瞬間から読み込まれる。伸ばすと画面に入る遠くから読み込まれることになる。早めに読み込みを開始すると大きなサイズなどの読み込みにかかる画像の待ち時間を減らせる。

画像の遅延読み込みには関係ないけれども画像に縦幅と横幅の片方があればもう一方を付けるようにしてある。ブラウザが画像の縦横比を知るとスムーズに表示できて高速化に役立つといわれるから行う。

縦幅と横幅に関してはloading-lazyのものと同じなので、「元のサイズ」で取り込んだ画像に新しく付ける方法などは割愛する。

通常の使用では問題がないけれども画像の横並びを行ったときにはみ出してレイアウトが崩れる不具合が確認された。

列指定のgrid-template-columnsで分割する単位に「fr」を使うと二段(縦二列)以上の場合に大きなサイズの画像が先に読み込まれたときに収まり切らずにはみ出す恐れがある。別の「%」では避けられたから投稿のグリッドレイアウトでは使わなくてはならない。

同時に隙間のgapを使うと幅に加算されてずれ易くなるので、そのときは計算式のcalc()でgapの値をセルの数で割ってそれぞれの「%」から引くと整えられる。

例えばgrid-template-columnsが「50% 50%」で、gapが「1em」ならば「calc(50% - 0.5em) calc(50% - 0.5em) 」にすると二つの同じ大きさのセルが「1em」の隙間で親ボックスにぴったり収まるようになる。

loading-lazyのプログラムでは画像のはみ出しを親要素から切り捨てることもできたけれどもIntersection Observeのプログラムではできない。

プログラムに使用しているJavaScriptのIntersection Observer APIのブラウザの対応状況(IntersectionObserver)は良好で、人気のブラウザの多くでは動作するけれども動作しない場合は何もせずに画面を含めて記事を本文を表示するようになっている。

関連:Bloggerの投稿画像の確実な遅延読み込みの自動化のJavaScriptプログラムの説明付きソースコード

YouTube動画も遅延読み込みする場合

基本のソースコードの二ヵ所を変更する。

変更①

const blimgs = npsbd.querySelectorAll("img, iframe");

※太字が追加部分。

変更②

if (blimgs) {
Promise.all([...blimgs].map(blimg => {
  (中略)
if ("IntersectionObserver" in window) postContentObserver.observe(result); else result.src = result.dataset.src; }); });  } else {
psbd.parentNode.replaceChild(npsbd, psbd); }

※打ち消し線が削除部分。

遅延読み込みの対象がiframeタグなので、YouTubeだけではなくて同じ要素を使ったものは画面外で止められることになる。

YouTubeだけにしたい場合は固有のidやclassをYouTubeの埋め込みのソースコードに付けておいてプログラムの変更①の「iframe」と置き換えて指定すれば大丈夫だ。

画像にdecoding-asyncを付ける場合

初回画面の遅延読み込みしない画像にHTMLのdecoding-asyncを付けると優先的に読み込まれて普通よりも早く表示できる。

基本のソースコードの一ヵ所を変更する。

JavaScript

if (entry.isIntersecting) {
const et = entry.target;
if (et.boundingClientRect.top < window.innerHeight) et.decoding = "async";
et.src = et.dataset.src; delete et.dataset.src; postContentObserver.unobserve(et); }

※太字が追加部分。

YouTube動画を追加した場合はdecoding-asyncを付けても優先的に読み込まれないから除外した方が良い。

JavaScript

if (entry.isIntersecting) {
const et = entry.target;
if (et.tagName === "IMG" && et.boundingClientRect.top < window.innerHeight) et.decoding = "async";
et.src = et.dataset.src; delete et.dataset.src; postContentObserver.unobserve(et); }

※太字が追加部分。

画像のdecoding-asyncのソースコードに「et.tagName === "IMG" &&」を追加する。

軽量化のWebPなどと併用するソースコード

ブログのパフォーマンスを上げるには画像URLのパラメーターを操作して軽量化のWebPのファイル形式に変えたり、ブラウザが保存して二回目以降に読み込まずに済ませられるキャッシュ期限を付けるとさらに効果的なので、遅延読み込みと併用する場合のソースコードを紹介する。

JavaScript

const bisrc = blimg.src, aroi = blimg.dataset.originalWidth / blimg.dataset.originalHeight;
if (/blogger\.googleusercontent\.com\/img\/a\//.test(bisrc)) {
blimg.dataset.src = /=.*$/.test(bisrc) ? bisrc + "-rw" : bisrc + "=rw"; } else {
if (/\.(jpg|jpeg)/i.test(bisrc)) {
const pmoi = /^.+\/(.*)\/.+$/.exec(bisrc);
blimg.dataset.src = pmoi[0].replace(pmoi[1], pmoi[1] + "-rw"); } else {
blimg.dataset.src = bisrc; }}
if (blimg.height) {
if (!blimg.width && aroi) blimg.width = blimg.height * aroi; } else if (blimg.width) {
if (aroi) blimg.height = blimg.width / aroi; } else if (aroi && !blimg.style.aspectRatio) {
blimg.style.aspectRatio = aroi; }
blimg.dataset.src = blimg.src; blimg.src = "";
return blimg; })).then(results => {

※太字が変更部分。

基本のソースコードに所定のソースコードを追加して不要な部分を削除する。

サンプルでは軽量化のWebPの「rw」と百日のキャッシュ期限の「e100」の画像パラメーターが付くようになっている。

画像パラメーターのカスタマイズについては遅延読み込みと同じように自動化を設定する個別ページで機能を含めて種々と説明している。

遅延読み込みのカスタマイズでYouTube動画を追加したり、decoding-asyncを付けたりする方法は通常の場合と変わらない。

記事のJavaScriptの実行用プログラム

記事の本文にscriptタグで記載したソースコードがある場合に止まってしまうので、実行できるようにするプログラムを追加する必要がある。

JavaScript

const pbscs = npsbd.querySelectorAll("script");
if (pbscs) {
pbscs.forEach(pbsc => {
const npbsc = document.createElement("script");
if (pbsc.src) npbsc.src = pbsc.src;
if (pbsc.async) npbsc.async = "async";
if (pbsc.textContent) npbsc.textContent = pbsc.textContent;
npsbd.appendChild(npbsc); }); }

遅延読み込みの基本のソースコードに続けて同じscriptタグの中に入れておく。

遅延読み込みの動作確認用のプログラム

遅延読み込みのプログラムは目に見えるものではないので、ブログにちゃんと組み込めたかどうかをチェックできるプログラムも紹介する。

JavaScript

results.forEach(result => {
result.onload = () => {
alert((result.tagName === 'IMG' ? '画像' : '動画') + "の読み込みが完了しました\u3002\ndecoding属性は\u300c" + result.getAttribute("decoding") + "\u300dの状態です\u3002"); }
if ("IntersectionObserver" in window) postContentObserver.observe(result); else result.src = result.dataset.src; });

※太字が変更部分。

画像や動画の読み込みが完了した時点で、「読み込みが完了ひました」というポップアップが出る。

初回画面の画像や動画はブログの記事を開いて直ぐに読み込みが完了するし、もっと下の画面外のものはスクロールで近付いたところから(容量が大きい画像やYouTube動画は少し待たされて)読み込みが完了するので、合わなければプログラムがちゃんと組み込まれてないと分かる。

decoding属性の値も示すので、付いていれば「async」、付いてなければ「null」と表示される。decoding-asyncを使用する場合、初回画面の画像に付いていて画面外の画像や全ての動画に付いてないことを確認できる。

コメント