ネイティブLazy-Load利用で、遅延ロードJSライブラリは不要?
いまだに「ネイティブLazy-Load Safariブラウザは iframe 要素(タグ)非対応」の記事をよく見かけるが…
Safari16.3以前も オプション設定 すれば、iPhone・iPad・Mac すべて対応済み なので、お間違い無く‼️
( IE のサポートが終了した今)主要なブラウザの新しいバージョンでは ネイティブLazy-Load 対応完了で良いと思う。(WebP画像対応を含め)
だが、ブラウザの ネイティブLazy-Load まかせで、もう 遅延ロードJSライブラリ の出番は無いのだろうか❓
2023年3月27日リリースされた Safari16.4 以降で、「ネイティブLazy-Load」が <img> に加え <iframe> デフォルト対応に‼️
初心者向けの「記事」を、追加しました❗️
PSI パフォーマンス・スコアが80点未満の方は…
主要ブラウザは、ネイティブLazy-Loadサポート済み
( IE のサポートが終了した今)モダンなブラウザの新しいバージョンでは ネイティブLazy-Load 対応完了で良いと思う。( WebP画像対応も )
最後に、SafariブラウザがネイティブLazy-Loadをサポート
2023年3月27日リリースされた Safari16.4 以降で、「ネイティブLazy-Load」が <img> に加え <iframe> デフォルト対応に‼️
Safari16.3以前、デフォルトでは img 要素のみの対応のため、iframe 要素対応は オプション設定 が必要‼️
一部オプション設定は必要だが、iPhone・iPad・Mac すべて、 img + iframe 要素「ネイティブLazy-Load」対応済み❗️
【 iPhone / iPad 】
【 Mac 】
ネイティブLazy-Loadの利点
loading="lazy"属性を追加するだけで、利用可能
<img> <iframe> タグに以下の属性を追加するだけ‼️
(ただし、height・width属性、あるいはインラインCSSにて 対象要素のサイズ設定は必要)
loading="lazy"
これだけで、スクロールして対象要素が画面領域に現れるとともにデータがダウンロードされ、表示される。
WordPressなら、自動適用
つまり、 loading="lazy" 追加もしなくて良い‼️
WordPress5.5から img要素に対して自動適用が始まり、その後 iframe要素も自動適用。
(ただし、height・width属性、あるいはインラインCSSにて 対象要素のサイズ設定は必要)
なお、ページ最初の img・iframe要素は自動的に適用を外してくれるが、ファーストビュー 完全対処には手動調整が必要な場合も…
JSライブラリの組み込み、不要
loading="lazy" 記述追加は必要だが( WordPressなら追加も不要 ) ネイティブLazy-Load はブラウザ機能のみで実現されるので、あなたのサイトに 新たな JSライブラリ組み込みは 不要。
JS無効時も、遅延ロード可能
JavaScript に依存しないため、JS無効状態でも ネイティブLazy-Load なら 遅延表示可能!
「JS無効化」状態のPCやスマホからの閲覧に備えるなら、JS無効時はJSライブラリも当然動作しないため ネイティブLazy-Load 一択となる。
noscript 要素と、"meta refresh"
当サイトは JS無効時の閲覧を諦めているので、 <noscript> タグを利用して、リダイレクトする。
「 JavaScript(JS) 無効時、 3秒後に JS有効化を促す別ページ にリダイレクトするサンプルコード 」は、以下。
実際に「 JavaScript 無効 」にブラウザの設定を切り替え後 スーパーリロードすれば リダイレクト画面が表示され、「一つ前の画面」に戻れば( JSライブラリも当然動作しないため )投稿記事に記述したままの状態(ネイティブLazy-Load利用)で画面表示されるはずだ。
<head>
<!-- 省略 -->
<!-- JavaScript 無効時 3秒後に 'JS有効化を促すページ' を表示 -->
<noscript>
<b:if cond='data:blog.url != "https://www.gadgets-geek.net/p/noscript.html"'>
<meta content='3; URL=/p/noscript.html' http-equiv='refresh'/>
</b:if>
</noscript>
</head>
noscript 要素は、JavaScript無効時のみ実行
7行め:metaタグは、body要素の中ではなく head要素の中に記述しなければ実行されないタグ
6,8行め:「リダイレクト先URLと異なる場合のみ、7行めを実行」するための 条件分岐 if 文(Google Blogger ブログの場合)
※ "Microsoft Bing Webmaster Tools" にて meta refresh は「警告」が出るため、ページの先頭にメッセージを表示する noscript サンプルに変更。
<body>
<noscript>
<p style='margin: 1rem;'>
<font color='red' size='6'>
<b>JavaScript has been "disabled" !!</b><br/>
<a href='/p/noscript.html'><b><i>Click THIS</i></b></a><br/>
<b>JavaScriptが 無効化 されています !!</b>
</font>
</p>
</noscript>
<!-- 省略 -->
</body>
ネイティブLazy-Loadの欠点
img・iframe要素(タグ)のみ、対応
両要素のみの対応のため、Prism.js や highlight.js など ソースコード表示用ライブラリ適用の <code> 要素の遅延ロードは非対応。
YouTube と Googleマップ は 静的な iframe 要素 利用のため問題ないが、 指定JS実行後 iframe 要素が動的生成 される 以下のような SNS埋め込み は 2023年2月末現在 非対応。
理論的には、動的生成された iframe要素に loading="lazy" 属性を自動付加すれば良いはずだが、Twitterは 動的生成されるタイミング把握が難しい気がする…
- Twitter ツイート埋め込みコード blockquote 要素 ➡️ iframe 要素を含む div 要素
- Twitter タイムライン埋め込みコード a 要素 ➡️ iframe 要素を含む div 要素
- Instagram 埋め込みコード blockquote 要素 ➡️ iframe 要素
- TikTok 埋め込みコード blockquote 要素 ➡️ iframe 要素
ブラウザまかせの機能のため、任意に設定できない
スクロールして対象要素が画面領域に現れるとともにレンダリング開始以外を選べないため、画面領域に入る前後からレンダリング開始とか 現状では「遅延ロードタイミング」を任意設定できない。
ネット速度とPCの処理能力が飛躍的に増大すれば、(瞬時に表示され)問題無いのかもしれない。
Safari ブラウザは デフォルト設定のままだと、img要素のみ適応
2023年3月27日リリースされた Safari16.4 以降で、「ネイティブLazy-Load」が <img> に加え <iframe> デフォルト対応に‼️
ネイティブLazy-Load はブラウザまかせの機能のため、ブラウザ設定で 非適用状態だと 遅延ロードが反映されない。
JSライブラリで遅延ロードを行うのであれば、ブラウザ設定に依存しないよう 実装されている場合が多いが…
Safari16.3以前の場合、iPhone ・ iPad ・ Mac 全デバイス iframeを遅延ロード対象にするには オプション設定 が必要‼️
Safari16.3以前の場合 新しいバージョンがリリースされるたびに(リセットされ)毎回 オプション設定 が必要なため、 iPhone や iPad などは iOS / iPadOS メジャーリリース時には少なくとも 再設定が必要だ。
なお、ブラウザが非対応か非適用状態だと、 loading="lazy" 属性は無視され 遅延ロードは適用されないが 表示自体はされる。
遅延ロードJSライブラリの必要性(ハイブリッド)
ネイティブLazy-Loadとの併用がオススメ
「 静的な img + iframe 」要素のみが遅延ロード対象であれば、ブラウザの ネイティブLazy-Load まかせで大丈夫。 (詳細は こちら を参照)
ただし、細かな設定やカスタマイズはできない。
そうでなければ、 まだまだ プラグイン、JSライブラリ に頼る必要が…
だが、JS無効状態のブラウザの場合、「JSライブラリのみ」だと何も表示されないか 低解像度の画像表示しかできないため、 併用が望ましい‼️
① 投稿記事にて img ・ iframeタグに「loading="lazy"」属性を記述しておけば、JS無効状態であっても ネイティブLazy-laod が適用され 最善の表示となる。
( WordPress の場合は、ページ最初の img ・ iframeタグを除き 「loading="lazy"」が自動付加される仕様 )
② JSライブラリ適用条件である data-src属性などのセット や CSSクラスの追加 は、JavaScript で行えば良い‼️
ネイティブLazy-Loadを、JSライブラリ対応に書き換えるJSサンプル
前提条件として、ブラウザがJS無効状態の場合でも ネイティブLazy-Load にて遅延ロード表示されるよう、投稿記事の img ・ iframe タグには「 loading="lazy" 」属性の追加が必要‼️
( WordPressバージョン5.9以降、両要素には 「 loading="lazy" 」属性が自動的に追加され、ページ最初の要素のみ自動除外 )
JSライブラリ対応のHTMLコードを投稿記事内に書かなければ、「JSライブラリの乗り換え」や「JSライブラリを廃止」する場合でも、投稿記事を書き換える必要は無い。
「ネイティブLazy-Load利用記述した img + iframe 要素」を、JSライブラリ用に書き換えるサンプルコード
(注)投稿記事を書き換えるのではなく、メモリ内の DOM を書き換える。
・loading="lazy" 属性が付加された img / iframe 要素のみを 抽出
・srcset属性や <picture>要素を利用する方は、改造が必要
① src属性の値(URL)を、data-src属性にコピー
② img要素のsrc属性の値には、BASE64エンコードの透明GIF画像をセット
③ iframe要素のsrc属性の値には、 "about:blank" をセット
④ 最後にloading属性を削除し、任意のCSSクラス名を img / iframe 要素に追加
( Google Blogger ブログ のように)パラメータ変更のみで 画像サーバが「低解像度の画像」を返す場合、② は「低解像度の画像」をセット(正規表現を利用したパラメータ置換)でも良い。
13行めを削除して「 8行めと 10行め 」の次に node.classList.add("xxx"); を1行ずつ追加すれば img と iframe で別々のCSSクラス名を追加できる。
"lazyload-img" , "lazyload-iframe" のように…
Nodeリスト(要素配列)が引数の「遅延ロードJSライブラリ」であれば querySelectorAll() 、HTMLコレクションが引数ならば getElementsByClassName() で対象要素を抽出すればよい。
17, 18行めは Defer.js ライブラリの「スクロール時現れるたびに要素を遅延ロード」する関数利用例。
const nodes = document.querySelectorAll('img[loading="lazy"], iframe[loading="lazy"]');
nodes.forEach(function(node) {
if ( !(node.tagName === 'IMG' || node.tagName === 'IFRAME') ) return;
if ( !node.hasAttribute('src') ) return;
node.setAttribute('data-src', node.src);
if (node.tagName === 'IMG') {
node.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
} else {
node.src = 'about:blank';
}
node.removeAttribute('loading');
node.classList.add('my-lazyload');
//console.info(node); //for Debug
});
Defer.dom('img.my-lazyload', 0, null, null, {rootMargin: "100%"});
Defer.dom('iframe.my-lazyload', 0, null, null, {rootMargin: "120%"});
① 1行めの引数は CSSセレクタ文字列 のため、必要があれば 以下のように 親要素で 限定。
筆者の利用テーマでは、投稿記事部分の div 親要素に「entry-text」CSSクラスが付加されるため…
'.entry-text img[loading="lazy"], .entry-text iframe[loading="lazy"]'
② 任意のCSSクラス名を追加するのは、JSライブラリ適用要素を 簡単に抽出可能 にするため
(注)JetThemeテーマには最初から Defer.js が組み込まれていて、Defer.dom() 関数を呼び出すのであれば(二重適用を防ぐため) "lazyload" のCSSクラス名は避けた方が良い。
③ Defer.jsライブラリの Defer.dom() 関数の第1引数は、CSSセレクタ(文字列)
同一条件の場合は、"img.my-lazyload, iframe.my-lazyload" とまとめて 1行で記述可能
おまけで、CSSクラス名を置換するコードも紹介‼️
Twitter と Instagram は指定JSを実行すると対象の全要素を一度に遅延ロードしてしまうため、スクロールして対象要素が現れるたびに遅延ロードするには「面倒な対応」が必要。
API を利用せず実現するには、「指定クラス名」を変更して一括遅延ロードを回避した後、自前でループ処理しなければならない。
(注)querySelectorAll() 関数は Nodeリスト が返されるため forEach でループ処理可能だが、 getElementsByClassName() 関数のような HTMLコレクション が返される場合 for でループ処理しなければならない。
(間接的に 配列 として取り扱えば、forEach ループも利用可能)
※ Nodeリストは静的だが HTMLコレクションは動的なため、以下のようなCSSクラス名の置換に HTMLコレクションを返す getElementsByClassName() 関数を利用すると、「すべてのCSSクラス名が置換されない」ので注意が必要。
「Nodeリスト と HTMLコレクション の違い」 も興味がある方は…
2023/04/29 すべてのCSSクラス名が置換されない 不具合を解決するため、getElementsByClassName() から querySelectorAll() 関数利用に変更。
const tw_EmbedTW = document.querySelectorAll('.twitter-tweet');
const tw_EmbedTL = document.querySelectorAll('.twitter-timeline');
const instaEmbed = document.querySelectorAll('.instagram-media');
tw_EmbedTW.forEach(function(node) { // Twitter TWeet
node.classList.replace('twitter-tweet', 'lazy-tweet');
});
tw_EmbedTL.forEach(function(node) { // Twitter TimeLine
node.classList.replace('twitter-timeline', 'lazy-timeline');
});
instaEmbed.forEach(function(node) { // Instagram
node.classList.replace('instagram-media', 'lazy-instagram');
});
01行め:引数はCSSセレクタ文字列で、CSSクラス名「twitter-tweet」を持つ全要素を抽出
06行め:CSSクラス名「twitter-tweet」を「lazy-tweet」に置換
指定JSロード後、基本 以下の関数
Twitter の場合、twttr.widgets.load(要素);
Instagram の場合、instgrm.Embeds.process(要素);
をループ処理すれば良いが、動的に要素が生成されるため 実コードは複雑
WordPressなら、プラグインという手段も…
『WordPress 遅延ロード プラグイン』などのキーワードで Google検索すれば、表示されるはず…
対応機能だけで満足できれば良いが、細かな設定やカスタマイズはできない。
オススメは、Defer.js
Twitter・Instagram・TokTokなどの SNS や、 Prism.js・highlight.jsなどの ソースコード表示用ライブラリ をサイトのページに埋め込む予定がある方は、( ネイティブLazy-Load と混在してもまったく問題は無いため )Defer.js 組み込み がオススメ‼️
オススメ具合は、筆者が公式ドキュメントを「翻訳」し、「利用方法」を説明した記事も書いたほど…
img iframe 要素や JS・CSSファイルだけではなく JS関数など ほとんど何でも遅延ロードでき、画面に表示される少し前から 遅延ロード させたり、遅延ロード時 要素ごとに(インライン)JS関数を実行 や、 遅延ロード後に CSSクラスを追加(つまり、CSS効果を遅延可能) するなどの オプション機能も用意済み。
レスポンシブ画像 と <picture>要素 対応も問題ないことを追記。
多機能なうえ、インラインでサイト組み込み時 わずか 1.81KByte (約1,810半角文字)の超コンパクト実装も、他の「遅延ロードJSライブラリ」と比較したメリットの一つ。
Pure JavaScript = Vanilla.js
( jQuery 不要 )
現在のWeb技術では、「遅延ロード」と「リソース(プリロード)ヒント」の組み合わせが最も効果的な選択肢らしい。
defer(遅延ロード)機能に加え、 Version: 3.2.0 以降 Defer.all() メソッドに リソース(プリロード)ヒント 機能を追加❗️
Defer.all() 関数の引数として指定した「JSファイル」の "preload" が、自動的に行われます‼️
有名な遅延ロードJSライブラリ
Defer.js 比較用として 2つのJSライブラリを紹介するが、lazysizes.js が約2年前から、lazyload.js に至っては 約4年前から アップデートされていない。
Defer.js は2019年 初リリースだが 頻繁にアップデートされ、少なくとも 毎年 新しい版がリリースされている。
lazysizes.js と lazyload.js の紹介記事も読んだが、Defer.js の作者はよく研究してるようで 機能的にも最強だ!
比較的新しい lazysizes.js では、読み込むタイミングをピクセルで指定可能だが、Defer.js と lazyload.js は Intersection Observer API の options をそのまま指定可能。
lazysizes.js でも背景画像をCSSで遅延読み込みするなら unveilhooksプラグインは不要だが、こちらもDefer.js 「遅延ロード後の対象要素に、任意のCSSクラスを付加」するサンプルのとおり、同等以上。
lazyload.js ライブラリの実行用 lazyload() 関数の 第1引数は 要素配列(Nodeリスト)、 第2引数は Intersection Observer API の options で、Defer.js ライブラリの Defer.dom() 関数は この改良版となる。
Intersection Observer API の options の説明は、以下の記事が図解で解りやすい。
なお、rootMarginは CSSのmargin のように、 "800px 0px" または "100% 0%" などの指定方法も可能。
( 2023/05/23 rootMargin には 絶対指定の場合 px 相対指定の場合 % を利用しますが、Chromeブラウザにて 0px または 0% と指定しないと不具合が発生する場合があるため 修正 )
最後まで読んでいただき、ありがとうございます。 また、お越しくださいませ。
// アタル