Bloggerの記事/追加ページの投稿画像や動画の遅延読み込みをIntersection Observer APIを使って自動化するJavaScriptプログラムの内容を説明する。
JavaScript
// 記事の本文の要素(psbd)と記事の本文の中身に付けたnoscript要素(blct)と記事の本文の要素の複製(npsbd)と新しいdiv要素(nl)の取得
const psbd = Blog1.querySelector("div.post-body"), blct = psbd.querySelector("noscript.blog-content"), npsbd = psbd.cloneNode(false), nl = document.createElement("div");
// 新しいdiv要素の高さを画面の縦幅と同じにする
nl.style.height = "100vh";
// 新しいdiv要素をnoscript要素の前に挿入する(無地の画面が最初に素早く出るようにしてプログラムの実行で記事の表示が遅れた際の違和感を減らすため)
psbd.insertBefore(nl, blct);
// 記事の本文の複製にnoscript要素のソースコードを入れる(noscript要素のソースコードはテキストだからそのままではHTMLとして扱えないため)
npsbd.insertAdjacentHTML("afterbegin", blct.innerText);
// 記事の本文の複製から画像要素を取得
const blimgs = npsbd.querySelectorAll("img");
// 画像の場合(画像か動画ありで振り分け済みの記事の本文から動画を除外するため/遅延読み込みに動画を含めるならば不要)
if (blimgs[0]) {
// 画像の配列をPromise.allとmapで並列処理
Promise.all([...blimgs].map(blimg => {
// 画像の縦横比(aroi)の取得
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; }
// 縦幅も横幅もなくて縦横比があってCSSの縦横比の指定のaspect-ratioがない場合、画面の横幅に対する画像の縦幅を算出して画像の位置(ブログの最上段からの距離)を推測するためにCSSのaspect-ratioを付ける
else if (aroi && !blimg.style.aspectRatio) {
blimg.style.aspectRatio = aroi; }
// 画像のalt属性が空だったら「イメージ」(日本語の場合)を記載する(Google検索のサイト評価のモバイルユーザビリティの警告を避けるため)
if (!blimg.alt) blimg.alt = "<data:messages.image/>";
// 画像要素のURLをカスタムデータへ移動してsrc属性から取り除いて画像要素を次のthen関数へ渡す
blimg.dataset.src = blimg.src; blimg.src = "": return blimg; })).then(results => {
// 記事の本文の複製を元の記事の本文(中身がnoscriptタグで止められたもの)と入れ換える(画像の位置を把握するために記事の本文を表示させるが、この時点ではsrc属性が空だから画像は読み込まれない)
psbd.parentNode.replaceChild(npsbd, psbd);
// 画像と画面の交差を監視するIntersection Observerのコンストラクター(postContentObserver)の設置
const postContentObserver = new IntersectionObserver(entries => {
// 監視する対象(画像要素)の配列の繰り返し処理
entries.forEach(entry => {
// 画面と交差した対象(画面に入った画像要素)の場合
if (entry.isIntersecting) {
// 監視する対象(画像要素)の取得
const et = entry.target;
// 監視する対象(画像要素)のsrc属性にカスタムデータのURLを移動して画像を表示してカスタムデータを削除して監視する対象(画像要素)の監視を終了する
et.src = et.dataset.src; delete et.dataset.src; postContentObserver.unobserve(et); }}); }, {
// 判定の余白を付ける(Intersection Observerコンストラクターのオプション)
rootMargin: "240px" });
// 画像の配列の繰り返し処理
results.forEach(result => {
// ブラウザがIntersection Observer APIに対応する場合は画像要素の監視を開始する
if ("IntersectionObserver" in window)
postContentObserver.observe(result);
// ブラウザがIntersection Observer APIに対応しない場合は画像要素のsrc属性にカスタムデータのURLを移動して画像を表示する
else result.src = result.dataset.src; }); }); }
// 動画の場合(遅延読み込みに動画を含めるならば不要)、記事の本文の複製を元の記事の本文(中身がnoscriptタグで止められたもの)と入れ換える(記事の本文が全て表示される)
else {
psbd.parentNode.replaceChild(npsbd, psbd); }