Defer.js 使い方 (利用方法)

Defer.js usage (CatchImage)

 Defer.js は、Shin さんが作成したインラインでも わずか1.81KByteの「遅延ロード」JavaScriptライブラリの決定版‼️

  Version: 3.4.0 以降 のセットアップ( JetTheme はバージョンアップ )方法を解説します。

 なお、WordPress 限定ですが、 お手軽な プラグイン 、 手動で制御されたい方向けの PHPライブラリ も Shin さんがリリースされています❗️

 以降、 Defer.js JavaScript( JS )ライブラリ利用方法の説明となりますので、ご注意ください。


 2023/05/23  Defer.dom()rootMargin 指定にて Chromeブラウザの場合、"150% 0" または "800px 0" のようにZERO指定すると不具合を起こす場合があるため、以下のように修正。
 "150% 0%" または "800px 0px"




はじめに

Defer.jsを組み込むべきか?

 遅延ロード 対象が  <img>  と  <iframe>  2要素のみの場合、ブラウザの「 ネイティブLazy-Loadで簡単に代用できるので、慌てて導入する必要はありません。

 「ネイティブLazy-Load」利用時の注意点は、以下の2つ!

  1. PSI 数値が悪くなるので、ファーストビュー の上記2要素の loading="lazy" 属性は外す
  2. Safariブラウザで バージョン 16.3以下 の場合  <img>  要素のみデフォルト対応のため、 <iframe>  要素対応は Safariブラウザの設定変更 が必要


  2023年3月27日リリースされた Safari16.4 以降で、「ネイティブLazy-Load」が <img> に加え <iframe> デフォルト対応に‼️‼️ 

iOS16.4(iPadOS16.4)以降 であれば、Safari16.4 以降を含みます

Mac の場合、(Safariのメニュー)[Safari] - [Safariについて] でSafariのバージョンを確認可能



しかし、 Twitter・Instagram・TokTokなどの SNS  や、 Prism.js・highlight.jsなどの ソースコード表示用ライブラリ  をサイトのページに埋め込む予定がある方は、( ネイティブLazy-Load と混在してもまったく問題は無いため )Defer.js 組み込み をオススメします‼️


 JS・CSSファイルだけではなくJS関数など ほとんど何でも遅延ロードでき、画面に表示される少し前から 遅延ロード させたり、遅延ロード時 要素ごとに(インライン)JS関数を実行 や、 遅延ロード後に CSSクラスを追加(つまり、CSS効果を遅延可能) するなどの オプション機能も用意されています。

 多機能なうえ、インラインでサイト組み込み時 わずか 1.81KByte (約1,810半角文字)の超コンパクト実装も、他の「遅延ロードJSライブラリ」と比較したメリットの一つです。



Package @shinsenter/defer.js

 Defer.js は、JavaScriptのマイクロライブラリで、(ほとんど)あらゆるものを遅延ロードさせることができます。
 Defer.js は依存性ゼロ、超効率的、そして Webバイタル に貢献します‼️

defer_logo_image

 Defer.js の全機能を知りたい方は 【公式】英語ドキュメント 、あるいは 日本語ドキュメント(筆者が翻訳)をお読みください。

 現在のWeb技術では、「遅延ロード」と「リソース(プリロード)ヒント」の組み合わせが最も効果的な選択肢らしいです。

  defer(遅延ロード)機能に加え、 Version: 3.2.0 以降 Defer.all() メソッドに リソース(プリロード)ヒント 機能が追加されました❗️ 




(JetTheme以外の)ブログ・ホームページへのセットアップ

 WordPress 限定ですが、 お手軽な プラグイン 、 手動で制御されたい方向けの PHPライブラリ も Shin さんがリリースされています❗️

 以降は、Defer.js JavaScriptJS )ライブラリの導入方法となります。


 WordPress にて「 Defer.js の全機能と最新バージョン 」を利用したい方は、JavaScript つまり  <script> 〜 </script>  を適切に埋め込むため、以下の記事をオススメします。

 また、WordPressなどでレンタルサーバを利用している場合、「WAFを一時的に無効」にするか 「WAF 除外登録」が必要です。
 レンタルサーバで有名な「ConoHa WING」と「Xserver」の WAF 設定方法を載せておきます。

 WAF:「Webアプリケーションファイアウォール」の略で、サーバ用の ファイアウォール 機能。
 この機能が「有効」だと 重要なファイルを変更できなかったり 変更結果が反映されない場合があるため、重要なファイル変更時のみ 一時的に「無効」にするか、自宅で利用している固定回線のipアドレスを「除外登録」します。

 「除外登録」するipアドレスが変わらなければ一度設定するだけで良いので、「除外登録」がオススメ。
 WAF「無効」のままだと 第三者から悪意のある攻撃を受ける可能性があるため、変更作業が終わったら 必ず WAF を「有効」に戻しましょう




とりあえず、JS を埋め込みたい方は…


JS埋め込み 4つの方法を解説(子テーマなど)…


条件分岐タグ利用で、JS埋め込み…




CDN利用のファイルロード

  <head>  要素内の最初の方に、以下の1行を挿入すると、Defer.jsライブラリ(ファイル)がダウンロードされ 組み込まれます。
 ( 右上の「Copy」を押すと、コピーできます )

  <script src="https://cdn.jsdelivr.net/npm/@shinsenter/defer.js@3.6.0/dist/defer.min.js"></script>

 【公式】ドキュメントどおりだと、以下のように <title>  要素の次で 挿入しています。

<head>
  <meta charset="UTF-8" />
  <title>文書のタイトル</title>

  <!-- ここに、挿入 -->
  <script src="https://cdn.jsdelivr.net/npm/@shinsenter/defer.js@3.6.0/dist/defer.min.js"></script>

  <!-- (省略) -->
</head>

  <body>  要素の最初の方に組み込んでも動作しますが、Defer()Defer.all() メソッド利用時に影響が出る場合があるため  <head>  要素の最初の方で組み込むのかと思います。
 Defer.jsライブラリに含まれる関数を利用するまでに、「Defer.jsライブラリのダウンロードと実行」が完了している必要がありますので…

 Defer.js の新しいバージョンがリリースされた場合は、 @3.6.0  の数字の部分を修正するだけで大丈夫です❗️



  <head>  要素内への JS組み込みは ページ内で最初に実行されますが、「(その間)他のHTML要素などの読み込みや解析が遅れる可能性」があります。
 わずか1.81KByteに最適化済みですが、次で説明する インライン組み込み を推奨します‼️
 少なくとも、( Defer.js ライブラリのダウンロードを行わず )HTTPリクエストを節約できますので…



インライン組み込み

Google Blogger以外のブログや、ホームページ

 ライブラリファイルのダウンロードを毎回行わず、HTTPリクエストを節約するには…

 defer.min.js から全テキストをコピー後、
以下7行めの「script要素内のコメント /*全テキストで置換*/ 」と置き換えることで、Defer.jsライブラリ全体をインライン化できます‼️
 (とてもコンパクトなファイルサイズ 1.81KByte に最適化済み)

 ※上記は v3.6.0 ですが、念のため 前バージョンの v3.4.0 リンクも添付

<head>
  <meta charset="UTF-8" />
  <title>文書のタイトル</title>

  <!-- ここに、インライン挿入 -->
  <script>
/*全テキストで置換*/
  </script>

  <!-- ... -->
</head>

JetTheme以外の Google Blogger用テンプレート(テーマ)

 インライン挿入は、以下のフォーマット( 6, 8行め:CDATAセクション付きの <script> </script> タグ )を利用してください。
 ( フォーマット以外は、上記のとおり )

 ※ Google Blogger ブログのテンプレート(テーマ)は HTMLファイルではなく XML(XHTML)ファイルのため、JSコード全体を CDATAセクション で囲む必要があります!
 XHTML形式のファイル内で、JavaScriptをインライン記述すると、<script> 要素内のコードまで解析されてしまうため、JSコード全体の 文字のエスケープ 処理が必要です。
 CDATAセクション で囲むと、JSコード全体の「文字のエスケープ」処理は必要ありません。

<head>
  <meta charset="UTF-8" />
  <title>文書のタイトル</title>

  <!-- ここに、インライン挿入 -->
<script>/*<![CDATA[*/
/*全テキストで置換*/
/*]]>*/</script>

  <!-- ... -->
</head>


JetTheme バージョンアップ

 Google Blogger用テンプレート(テーマ)の 「 JetTheme(無料・有料版)」は、defer.js@2.5.0一部カスタマイズしたものを インライン組み込みしています。

 推奨はバージョンアップですが、バージョンアップせず そのままで利用する場合、Defer.js()Defer.css()Defer.dom() 3関数は利用できるはずです。
 (注) Defer.dom() 関数を利用する場合は、無条件 4つめの引数として null を追加します‼️
そのため、
 本来4つめの引数➡️5つめの引数
 本来5つめの引数➡️6つめの引数
として、セットしなければいけません。



 JetTheme は、Defer.dom() 関数の引数を1つ増やし、6個の引数(パラメータ)を持つようカスタマイズ されています。
 ブラウザの Console で追加された引数のデータを表示してみましたが、筆者の能力では詳細は理解できませんでした。


 JetTheme 動作の互換性を保つため、defer.js@3.4.0 以降
Defer.dom():JetTheme作者版(6個の引数)
Defer.dom2()Defer.js作者版(5個の引数)
の関数名で利用できるよう、当管理人(アタル)がコードをカスタマイズしています‼️

  つまり、ユーザーが Defer.dom2() 記述した場合のみ、Defer.js作者オリジナルの関数が呼び出されます。




 テンプレート(XMLファイル)バックアップ方法編集方法が分からない方は、以下の記事をお読みください。




 以下は、defer.js@3.6.0 版コードを JetTheme 用にカスタマイズしたもの。(2.11KByte)
6個の引数を持つ a.dom=function(n,t,c,i,u,r)
5個の引数を持つ a.dom2=function(n,t,c,i,u)
の 2関数 が見て取れますね。

/*!@shinsenter/defer.js@3.6.0*/
!function(c,f,s){function a(n,t,e){k?S(n,t):((e=e===s?a.lazy:e)?N:C).push(n,Math.max(e?350:0,t))}function i(n){j.head.appendChild(n)}function l(n,t){n.forEach(function(n){t(n)})}function u(t,n,e,o){l(n.split(" "),function(n){(o||c)[t+"EventListener"](n,e||p)})}function r(n,t,e,o){return(o=t?j.getElementById(t):s)||(o=j.createElement(n),t&&(o.id=t)),e&&u(g,b,e,o),o}function d(n,t){l(q.call(n.attributes),function(n){t(n.name,n.value)})}function h(n,t){return q.call((t||j).querySelectorAll(n))}function m(o,n){l(h("source,img",o),m),d(o,function(n,t,e){(e=/^data-(.+)/.exec(n))&&o[x](e[1],t)}),n&&(o.className+=" "+n),o[b]&&o[b]()}function n(n,t,e){a(function(o){l(o=h(n||"script[type=deferjs]"),function(n,e){n.src&&(e=r(v),d(n,function(n,t){n!=I&&e[x]("src"==n?"href":n,t)}),e.rel="preload",e.as=y,i(e))}),function n(t,e){(t=o[E]())&&(e=r(y),d(t,function(n,t){n!=I&&e[x](n,t)}),e.text=t.text,t.parentNode.replaceChild(e,t),e.src&&!e.getAttribute("async")?u(g,b+" error",n,e):n())}()},t,e)}function p(n,t){for(t=k?(u(e,o),N):(u(e,w),k=a,N[0]&&u(g,o),C);t[0];)S(t[E](),t[E]())}var v="link",y="script",b="load",t="pageshow",g="add",e="remove",o="touchstart mousemove mousedown keydown wheel",w="on"+t in c?t:b,x="setAttribute",E="shift",I="type",A=c.IntersectionObserver,j=c.document||c,k=/p/.test(j.readyState),C=[],N=[],S=c.setTimeout,q=C.slice;a.all=n,a.dom=function(n,t,c,i,u,r){a(function(e){function o(n){u&&!1===u(n)||m(n,c)}e=A?new A(function(n){l(n,function(n,t){n.isIntersecting&&(t=n.target)&&(i&&i(t),e.unobserve(t),o(t))})},r):s,l(h(n||"[data-src]"),function(n){n[f]||(n[f]=a,e?e.observe(n):o(n))})},t,!1)},a.dom2=function(n,t,c,i,u){a(function(e){function o(n){i&&!1===i(n)||m(n,c)}e=A?new A(function(n){l(n,function(n,t){n.isIntersecting&&(e.unobserve(t=n.target),o(t))})},u):s,l(h(n||"[data-src]"),function(n){n[f]||(n[f]=a,e?e.observe(n):o(n))})},t,!1)},a.css=function(t,e,n,o,c){a(function(n){(n=r(v,e,o)).rel="stylesheet",n.href=t,i(n)},n,c)},a.js=function(t,e,n,o,c){a(function(n){(n=r(y,e,o)).src=t,i(n)},n,c)},a.reveal=m,c[f]=a,k||u(g,w),n()}(this,"Defer");

jt_edit_before

 上記画面にて、(先頭行など)編集画面のコード内でクリックしてキャレット(テキストカーソルとも呼ばれる)表示後、[Ctrl]+[F] (Macの場合、[command]+[F]) にて defer.js を検索して 以下画面のように 上記コードCopyにて 置き換えます。
  'IntersectionObserver'in window  の前(  ;  の後)で改行しておくと、次回のバージョンアップが簡単ですね。


 以下の画面は、 JetTheme版 defer.js@3.5.0 コードに置き換えた場合です。

jt_edit_after

 JetTheme用 defer.js@3.6.0defer.js@3.4.0 を筆者がファイルでも提供していますので、上記リンクも ご利用ください。



 ※ 上記作業で気付いたかもしれませんが、 JetTheme では  <body>  要素内に Defer.js が組み込まれています‼️ 

 jtCallback() 関数は、(少なくとも、Defer.jsライブラリの実行完了後)適切なタイミングでコールバックされるよう、JetThemeテンプレート(テーマ)作者が用意した 特別な関数です。

 したがって、JetTheme にて( Defer.jsライブラリを  <head>  要素内に組み込まない ) デフォルト組み込み時、jtCallback() 関数内に Defer.jsライブラリ実装の関数群を 記述しなければいけません。



JetThemeのDefer.jsを head要素内に組み込む

 ※ JetTheme では  <body>  要素内に Defer.js を組み込んでいるため、 Defer()Defer.all() 関数を利用するのであれば  <head>  要素内に移動した方が無難です!

 上記画面の「2410〜2411行め」を丸ごと 切り取りJetTheme 以外の Google Blogger 用テンプレート(テーマ) のとおりに 貼り付け てください。

 筆者は Defer.css()Defer.js()Defer.dom() の3関数のみ利用のため、 <head>  要素内に移動していませんが… なお、preload は「手動」で追加 しています。





Defer.js利用方法

 以下では、 Defer.css()Defer.js()Defer.dom() の3関数のみを利用したサンプルコードを掲載します❗️

 その他の関数などは、概略のみ説明いたします。


  JetTheme 利用者は、この説明 を まずは お読みください‼️

 JetTheme の場合、既存の jtCallback() 関数内に、「 Defer.jsライブラリ 」の関数を記述します。

 筆者の jtCallback() 関数のコード全文 をサンプルとして、掲載します❗️
 上記関数内に記述したJSコードは、「JetTheme テンプレート(テーマ)以外を利用の方」も 参考になるはずです。
 ( Defer.dom2 は Defer.dom に、関数名の置換が必要です )



img 要素

 ネイティブLazy-Load と比べ Defer.js は、「画面に表示される少し前からロード」や「要素(グループ)ごとの遅延時間の変更」など細かい調整が可能です❗️

  1. src → data-src
  2. 領域確保用の仮画像を src に設定
  3. CSSセレクタで抽出できるよう、CSSクラス や id を追加

(注)ブラウザが JS無効状態の場合は src="〜" 画像表示のまま で、 data-src"〜" 画像には置き換わりません。

 ※ CSSセレクタ チートシート(図解で解りやすいですが、部分一致前方一致の2箇所のコードが間違ったままですね)


<div class="defer-img">
  <img alt="image-description" height="640" width="640" data-src="https://〜.webp" title="画像のタイトル" src='data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' />
</div>

 以下は、インラインCSSを利用して、領域を指定する場合

<div class="defer-img">
  <img alt="image-description" style="height:640px; width:640px;" data-src="https://〜.webp" title="画像のタイトル" src='data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' />
</div>

 1. Defer.js で遅延ロードを行うには、URLを含む属性の先頭に data- を付加

 2. レンダリング前後で表示位置がずれないよう、height width 属性 または インラインCSSにて領域を指定して、仮画像を表示
 上記サンプルでは、ダウンロード無しで利用可能な「base64エンコードした画像」を設定。

 3. 遅延ロード完了まで低解像度の画像を表示させたい場合は、 src="" に「オリジナル画像の低解像度版」をセット

 4. CSSセレクタ ".defer-img img" で対象要素を抽出するため、"defer-img" CSSクラス付きの divタグなど 親要素 を追加(  子+孫のimg要素  が対象 )


 一般的には </body> タグ直前で、以下のJavaScript(JS)を実行!

  • 100ミリ秒 遅延後に、対象要素をロードしてレンダリング
  • 最後の引数ビューポート領域が 100% のため "100%" だと上下左右 画面1個分(高さor幅) margin が付き、その分手前から レンダリングが開始されます。
     rootMargin には margin 同様、"100% 0%" や 上下左右で異なる値 も指定可能。
     2023/05/23  rootMargin には 絶対指定の場合 px 相対指定の場合 % を利用しますが、Chromeブラウザにて 0px または 0% と指定しないとエラーになる場合があるため 修正しました )
  • 対象要素が複数存在する場合、CSSクラスの追加やJS関数の実行 をループ処理可能
<script>
  Defer.dom('.defer-img img', 100, null, null, {rootMargin: "150% 0%"});
</script>


JetTheme img 要素

<div class="defer-img separator">
  <img alt="defer_logo_image" data-original-height="640" data-original-width="640" height="640" width="640" data-src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF0Ta5W1JpDBbxXZdV6eFykZ4z_wbFOc6SYmu5mSNLI9kE6boujDzALHx9pDx7GSkU20BpBcm6ohieEBpDTt3SPT64zyt7Ji5JRUvNhtZE_30dVHQCm6veXfS7gP-g8bddJMS-UfltIwaROj6vpnEoyOqbyWmZjK96SjYyyJJdR3C0Kb4kZvnsU9ptMg/s0-rw/defer360_logo.webp" title="Defer.js ロゴ画像" src='data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' />
</div>

 Google Blogger ブログの画像サーバに WebPフォーマット画像をアップロード後 そのままの解像度でWebP画像をロードするため、「/s0-rw/」パラメータを利用。 その他の説明は、こちら をお読みください。


 以下は遅延ロード完了まで、低解像度の画像(src="〜")を表示するサンプルコード。

 Google Blogger ブログの画像サーバは「/s0-rj-l10/」パラメータ変更のみで(JPEGフォーマット変換後に)ファイルサイズを圧縮可能で、例だと「JPEG変換後、品質レベル10に圧縮した画像」がダウンロードされます。
 ( JSにて 正規表現を利用してパラメータ部分を置換すれば、data-src のURLから src のURLを作成できるでしょう )

 ブラウザが JS無効状態の場合は src="〜" 画像表示のまま で、 data-src"〜" 画像には置き換わりません。

<div class="defer-img separator">
  <img alt="defer_logo_image" data-original-height="640" data-original-width="640" height="640" data-src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF0Ta5W1JpDBbxXZdV6eFykZ4z_wbFOc6SYmu5mSNLI9kE6boujDzALHx9pDx7GSkU20BpBcm6ohieEBpDTt3SPT64zyt7Ji5JRUvNhtZE_30dVHQCm6veXfS7gP-g8bddJMS-UfltIwaROj6vpnEoyOqbyWmZjK96SjYyyJJdR3C0Kb4kZvnsU9ptMg/s0-rw/defer360_logo.webp" title="Defer.js ロゴ画像" width="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF0Ta5W1JpDBbxXZdV6eFykZ4z_wbFOc6SYmu5mSNLI9kE6boujDzALHx9pDx7GSkU20BpBcm6ohieEBpDTt3SPT64zyt7Ji5JRUvNhtZE_30dVHQCm6veXfS7gP-g8bddJMS-UfltIwaROj6vpnEoyOqbyWmZjK96SjYyyJJdR3C0Kb4kZvnsU9ptMg/s0-rj-l10/defer360_logo.webp" />
</div>

 JetTheme では、XMLファイル内 jtCallback() 関数内に「Defer.js関連のJSコード」を記述します❗️
 以下画像だと、2482行めの箇所に、JSコードをどんどん追加。

(注意)v3.4.0 以降の「JetTheme版 Defer.js コード」を組み込んだ方は、引数5個のオリジナルの Defer.dom() 関数を利用したい場合、以下のように関数名を  Defer.dom2  に変更しなければいけません‼️

jtCallback-func
  Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "150% 0%"});

 rootMargin には margin 同様、"100% 0%" や 上下左右で異なる値 も指定可能。
 2023/05/23  rootMargin には 絶対指定の場合 px 相対指定の場合 % を利用しますが、Chromeブラウザにて 0px または 0% と指定しないとエラーになる場合があるため 修正しました。




iframe 要素

 先に、「img 要素」の説明 をお読みください。

 ネイティブLazy-Load と比べ Defer.js は、「画面に表示される少し前からロード」や「要素(グループ)ごとの遅延時間の変更」など細かい調整が可能です❗️

  1. src → data-src
  2. src="about:blank" を追加
  3. CSSセレクタで抽出できるよう、CSSクラス や id を追加

(注)ブラウザが JS無効状態の場合は src="〜" 表示のまま で、 data-src"〜" 表示には置き換わりません。


<div class="defer-iframe">
  <iframe frameborder="0" scrolling="no" style="height: 120px; width: 580px; max-width: 100%; vertical-align:top;" data-src="https://richlink.blogsys.jp/embed/c68a00f0-dbea-3393-b3a7-e27fad08b2f6" src="about:blank" ></iframe>
</div>

 1. Defer.js で遅延ロードを行うには、URLを含む属性の先頭に data- を付加

 2. レンダリング前後で表示位置がずれないよう、height width 属性 または インラインCSSにて領域を指定

 3. CSSセレクタ ".defer-iframe iframe" で対象要素を抽出するため、"defer-iframe" CSSクラス付きの divタグなど 親要素 を追加(  子+孫のiframe要素  が対象 )


 一般的には </body> タグ直前で、以下のJavaScript(JS)を実行!

  • 100ミリ秒 遅延後に、対象要素をロードしてレンダリング
  • 最後の引数ビューポート領域が 100% のため "100%" だと上下左右 画面1個分(高さor幅) margin が付き、その分手前から レンダリングが開始されます。( margin 同様、"100% 0%" などの指定可 )
  • 対象要素が複数存在する場合、CSSクラスの追加やJS関数の実行 をループ処理可能
<script>
  Defer.dom('.defer-iframe iframe', 100, null, null, {rootMargin: "150% 0%"});
</script>

 遅延時間やCSSクラス指定などの条件が同じ場合は、以下のように まとめることも可能。
 img要素 と iframe要素 の遅延ロードを、1つの Defer.dom() 関数で処理。
 CSSセレクタは カンマ( , )で区切ると、複数指定可能。

<script>
  Defer.dom('.defer-img img, .defer-iframe iframe', 100, null, null, {rootMargin: "150% 0%"});
</script>


JetTheme iframe 要素

<div class="defer-iframe">
  <iframe frameborder="0" scrolling="no" style="height: 120px; width: 580px; max-width: 100%; vertical-align:top;" data-src="https://richlink.blogsys.jp/embed/c68a00f0-dbea-3393-b3a7-e27fad08b2f6" src="about:blank" ></iframe>
</div>

 JetTheme での注意点は、以下のとおり  Defer.dom2  関数名への変更のみ!
その他の説明は、こちら をお読みください。


  Defer.dom2('.defer-iframe iframe', 100, null, null, {rootMargin: "150% 0%"});


Twitter

  ツイート埋め込みコードの blockquote要素、タイムライン埋め込みコードの a要素 後の 
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
 は、全部 削除してください‼️ 



 1000ミリ秒=1秒後に、全ツイートとタイムラインをレンダリングするコードは以下。

  Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 1000);

 Defer.js() 関数は 5個めの最終引数に true を指定すると、(マウス操作など)ユーザーがアクションを起こすまで、指定JSのロードが保留されます。

  Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 1000, null, true);

 指定JSロード後、(インライン)JS関数を実行する例は、以下。
 画面表示領域にCSSセレクタ指定要素が現れるたびに要素をレンダリングする、Defer.dom() 関数 を実行。( JetThemeの場合、Defer.dom2() を利用 )

  const tw_EmbedTW = document.getElementsByClassName('twitter-tweet');		// Twitter TWeet
  const tw_EmbedTL = document.getElementsByClassName('twitter-timeline');	// Twitter TimeLine

  if (tw_EmbedTW.length !== 0 || tw_EmbedTL.length !== 0) {
    Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
      Defer.dom('.twitter-tweet, .twitter-timeline', 500, null, twttr.widgets.load, {rootMargin: "150% 0%"});
    });
  }

 デフォルトのCSSクラスが付与される ツイート埋め込みコードは blockquote要素、タイムラインは a要素ですが、指定JSが実行されると 同じCSSクラスが付与された(子・孫要素として、iframe 要素を持つ) div要素が動的に生成されます。(元の blockquote / a 要素は削除)
 Twitterの場合、Defer.dom() 関数の遅延時間を 500〜1000(ミリ秒)で指定すると、 div要素が遅延対象になります。
 筆者の調整では、ページごとのTwitter埋め込み数が多いほど、この遅延時間を増やした方が良いようです。

 そのため、Defer.dom() 関数のCSSセレクタは(要素指定無しで)「クラス名のみ」の指定が無難です‼️
 CSSセレクタは カンマ( , )で区切ると、複数指定可能。

 なお Twitter は、指定JS実行後に Defer.dom() を実行しても、レンダリングのタイミングが早まるだけで 全・対象要素が一度にレンダリングされる仕様(一括レンダリング) のようです。

 要素ごとに実行される関数名が判明している場合、(少し面倒ですが)スクロールするたびに要素をレンダリング 可能です。
 4つめの引数 twttr.widgets.load は、引数として「要素(Node)」を持つ関数名。
 そのため、以下のように記述可能。

  Defer.dom('.twitter-tweet, .twitter-timeline', 500, null, function(element) {
    twttr.widgets.load(element);
  }, {rootMargin: "150% 0%"});


Instagram

  インスタ埋め込みコードの blockquote要素 後の 
<script async src="//www.instagram.com/embed.js"></script>
 は、全部 削除してください‼️ 



 1000ミリ秒=1秒後に、全インスタ埋め込みをレンダリングするコードは以下。

  Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000);

 Defer.js() 関数は 5個めの最終引数に true を指定すると、(マウス操作など)ユーザーがアクションを起こすまで、指定JSのロードが保留されます。

  Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, null, true);

 指定JSロード後、(インライン)JS関数を実行する例は、以下。
 画面表示領域にCSSセレクタ指定要素が現れるたびに要素をレンダリングする、Defer.dom() 関数 を実行。( JetThemeの場合、Defer.dom2() を利用 )

  const instaEmbed = document.getElementsByClassName('instagram-media');	// Instagram

  if (instaEmbed.length !== 0) {
    Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, function() {
      Defer.dom('.instagram-media', 100, null, instgrm.Embeds.process, {rootMargin: "150% 0%"});
    });
  }

 デフォルトのCSSクラスが付与される インスタ埋め込みコードは blockquote要素ですが、指定JSが実行されると 同じCSSクラスが付与された iframe要素が動的に生成されます。(元の blockquote要素は削除)

 そのため、Defer.dom() 関数のCSSセレクタは(要素指定無しで)「クラス名のみ」の指定が無難です‼️

 なお Instagram は、指定JS実行後に Defer.dom() を実行しても、レンダリングのタイミングが早まるだけで 全・対象要素が一度にレンダリングされる仕様(一括レンダリング) のようです。

 要素ごとに実行される関数名が判明している場合、(少し面倒ですが)スクロールするたびに要素をレンダリング 可能です。
 4つめの引数 instgrm.Embeds.process は、引数として「要素(Node)」を持つ関数名。
 そのため、以下のように記述可能。

  Defer.dom('.instagram-media', 100, null, function(element) {
    instgrm.Embeds.process(element);
  }, {rootMargin: "150% 0%"});


TikTok

  TikTok埋め込みコードの blockquote要素 後の 
<script async src="https://www.tiktok.com/embed.js"></script>
 は、全部 削除してください‼️ 



 1000ミリ秒=1秒後に、全TikTok埋め込みをレンダリングするコードは以下。

  const tiktokEmbed = document.getElementsByClassName('tiktok-embed');	// TikTok

  if (tiktokEmbed.length !== 0) Defer.js('https://www.tiktok.com/embed.js', 'tiktok-js', 1000);

 Defer.js() 関数は 5個めの最終引数に true を指定すると、(マウス操作など)ユーザーがアクションを起こすまで、指定JSのロードが保留されます。

  Defer.js('https://www.tiktok.com/embed.js', 'tiktok-js', 1000, null, true);

 TikTok の 要素ごとのレンダリング関数名 をご存知の方は、この記事のコメントや Twitter などで筆者に連絡して頂けると嬉しいです。
 よろしくお願いいたします!



YouTube

 ネイティブLazy-Load と比べ Defer.js は、「画面に表示される少し前からロード」や「要素(グループ)ごとの遅延時間の変更」など細かい調整が可能です❗️

  1. src → data-src style → data-style
  2. src="about:blank" を追加
  3. CSSセレクタで抽出できるよう、CSSクラス や id を追加
  4. (YouTubeは動画id指定でその画像を取得可能なため、背景画像をCSS設定)

<div class="defer-youtube">
  <iframe title="The new MacBook Air"
          width="480" height="270" frameborder="0" allowfullscreen=""
          allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
          src="about:blank"
          data-src="https://www.youtube.com/embed/jwmS1gc9S5A"
          data-style="background: transparent url(https://img.youtube.com/vi/jwmS1gc9S5A/hqdefault.jpg) 50% 50% / cover no-repeat;">
  </iframe>
</div>

 1. Defer.js で遅延ロードを行うには、URLを含む属性の先頭に data- を付加

 2. レンダリング前後で表示位置がずれないよう、height width 属性 または インラインCSSにて領域を確保

 3. CSSセレクタ ".defer-youtube iframe" で対象要素を抽出するため、"defer-youtube" CSSクラス付きの divタグなど 親要素 を追加(  子+孫のiframe要素  が対象 )


 一般的には </body> タグ直前で、以下のJavaScript(JS)を実行!

  • 100ミリ秒 遅延後に、対象要素をロードしてレンダリング
  • 最後の引数ビューポート領域が 100% のため "100%" だと上下左右 画面1個分(高さor幅) margin が付き、その分手前から レンダリングが開始されます。( margin 同様、"100% 0%" などの指定可 )
  • 対象要素が複数存在する場合、CSSクラスの追加やJS関数の実行 をループ処理可能
  • レンダリング後、iframeの各要素に "youtube-loaded" CSSクラスを追加
<script>
  Defer.dom('.defer-youtube iframe', 100, 'youtube-loaded', null, {rootMargin: "150% 0%"});
</script>

 遅延時間やCSSクラス指定などの条件が同じ場合は、以下のように まとめることも可能。

<script>
  Defer.dom('.defer-iframe iframe, .defer-youtube iframe', 100, null, null, {rootMargin: "150% 0%"});
</script>

 画面表示領域にCSSセレクタ指定要素が現れるたびに要素をレンダリングする、Defer.dom() 関数 を実行。( JetThemeの場合、Defer.dom2() を利用 )



YouTube表示のPSIスコア対策


 YouTube埋め込みは Lazy-Load処理しても PSIスコアが悪化する場合があるため(特に、ネイティブLazy-Load 時)、埋め込み表示ではありませんが その代替方法を紹介します‼️
 アクションを起こさない限り「動画データ+YouTube用のJS」をダウンロードしないため、PSIスコアは悪化せず FirstView でも利用可能です。

 YouTube の動画idから、「画像」を取得可能です。
 その画像をクリック(タップ)したら、「別タブ または アプリ」で 動画を再生します。

 その画像をクリック(タップ)したら「iframe要素・埋め込み」に置き換える方法もありますが、JSをかなり記述しないといけないため 以下に「記事リンク」のみ貼っておきます。




 コード説明の前に、まずはサンプルを‼️
YouTube の動画idから取得した画像の上に、フリーでダウンロード可能な「再生ボタン」を重ねています。



<iframe loading="lazy" width="480" height="270" src="https://www.youtube.com/embed/P-vR-8Mx4Dk" title="待望すぎるアップデート! iOS16.4新機能まとめ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen>
</iframe>

 上記は、「YouTube埋め込みコード」❗️
 loading属性・title属性を設定し、width と height を 16:9 の比率で適当なサイズに変えています。

 srcのURL内  /embed/  以降が「動画id」で、このサンプルだと P-vR-8Mx4Dk となります。

 //i.ytimg.com/vi/ 動画 id /mqdefault.jpg のURLで画像を取得できるため、このサンプルだと //i.ytimg.com/vi/P-vR-8Mx4Dk/mqdefault.jpg です。



 さきほどのサンプル表示の HTMLコードと CSSコードは、以下❗️

 Google Blogger の画像サーバは パラメータ変更のみで「サイズ・フォーマット・品質など」を変更可能でとても便利なのですが、URLが長いのだけはなんとかして欲しいものです…

<div class="movie" >
  <a href="//www.youtube.com/watch?v=P-vR-8Mx4Dk" target="_blank" >
    <img loading="lazy" width="480" height="270" src="//i.ytimg.com/vi/P-vR-8Mx4Dk/mqdefault.jpg" alt="YouTube-movie-image" title="YouTube:待望すぎるアップデート! iOS16.4新機能まとめ" >
    <img loading="lazy" class="overlap-center" width="100" height="100" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkcDl-9PS9iDhhQfTPB363hnYDFTknOEy8YzzBHw63-h-2UFmFW9bvimESmyANjrWxzSUB14POLsCQPl-mic4nEEbBCsYJg_aY_KBt2Gle89uL1Hd1RwV-IIwcEe4SlmY7sOw0MmImpDy9aL_uAa6-9AwvkKPhY2VGCckfDoJfhYTxhMpP6lODwdHHLA/s0-rw/movie_play.webp" alt="movie_play_icon" >
  </a>
</div>

1行め:親の div 要素には、CSSクラス「movie」を付与
2行め:動画idが P-vR-8Mx4Dk の場合、hrefのURLは //www.youtube.com/watch?v=P-vR-8Mx4Dk
3行め:先ほど説明した「YouTube画像のURL」をsrc属性にセットし、alt・title属性のセットも推奨
4行め:「YouTube画像」の上に「動画の再生ボタン」を重ねるため、重ねる方の img 要素には CSSクラス「overlap-center」を付与(alt属性のセット推奨)

(注)FirstView 以外で 両画像を「ネイティブLazy-Load」で遅延ロードする場合、loading="lazy" 属性を付加します。

div.movie {
	position: relative;
	text-align: center;
	max-width: 100%;
}
.overlap-center {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
}
div.movie img:hover {
	opacity: 0.5 ;
}

 スマホだと「YouTubeアプリ」、PCだと「別タブ」で動画が再生されます‼️
 「埋め込み表示」にこだわる方以外は、この方法で大丈夫かと…


 YouTube画像の上に重ねている「動画の再生ボタン」画像は、以下のサイトから頂きました❗️




Prism.js

  // Prism.js ( クラス language- 部分一致 )
  const prismEmbed = document.querySelectorAll('[class*="language-"]');

  if (prismEmbed.length !== 0) {
    Defer.css('https://past.gadgets-geek.net/css/prism.css', 'prism-css', 700);
    Defer.js('https://past.gadgets-geek.net/js/prism129.js', 'prism-js', 700);
  }
  // Prism.js ( クラス language- 部分一致 )
  const prismEmbed = document.querySelectorAll('[class*="language-"]');

  if (prismEmbed.length !== 0) {
    window.Prism = window.Prism || {};	// グローバル変数 window.Prismオブジェクトを初期化
    Prism.manual = true;				// 全Code要素を自動的にハイライトしないモードに変更
    Defer.css('https://past.gadgets-geek.net/css/prism.css', 'prism-css', 700);
    Defer.js('https://past.gadgets-geek.net/js/prism129.js', 'prism-js', 700, function() {
      Defer.dom('pre code', 100, null, Prism.highlightElement, {rootMargin: "120% 0%"});
    });
  }

 05行め:window.Prism オブジェクトを初期化
( window.xxx:グローバル変数 ➡️ 06行めは window.Prism.manual = true; の省略記述 )
 v = v || {};  は JSイディオム で、v が undefined の時など(型変換後、条件式でfalse)に、デフォルト値として「空のオブジェクト」を設定
 "" 0 undefined null false 5種類の値は、条件式で false と評価
 {} "hoge" 1 -1 [] true  6種類の値は、条件式で true と評価

 window.Prism オブジェクトを初期化後 Prism.manual = true; にしないと、指定JSロードでページ内の全要素がレンダリングされます。
 Prism.manual = true; に変更した場合、Defer.dom() 関数 などを利用して 要素ごとの「ロード+レンダリング」を行う必要があります。(CSSセレクタで、pre要素の 子・孫要素のcode要素を 指定)

 指定JSロード後に実行される(インライン)コールバック関数内に記述した Defer.dom() 関数の4つめの引数 Prism.highlightElement は「要素ごとに実行される関数名」のため、以下のようにも書けます。

  Defer.dom('pre code', 100, null, function(element) {
    Prism.highlightElement(element);
  }, {rootMargin: "120% 0%"});

 画面表示領域にCSSセレクタ指定要素が現れるたびに要素をレンダリングする、Defer.dom() 関数 を実行。( JetThemeの場合、Defer.dom2() を利用 )



highlight.js

var base = 'https://cdn.jsdelivr.net/npm/highlightjs@9.12.0';

Defer.css(base + '/styles/rainbow.css', 'hljs-css', 700);
Defer.js(base + '/highlight.pack.min.js', 'hljs-js', 700, function () {
  hljs.initHighlightingOnLoad();	//バージョン 10.5 まで
});

 全対象要素を 一度にレンダリングする場合、「JSロード後に指定関数の呼び出し」が必要です!
 05行め:「highlight.js」ライブラリの新しいバージョンでは
 hljs.initHighlightingOnLoad(); ではなく、代わりに hljs.highlightAll(); を利用してください。

 Defer.js() 関数の4つめの引数は「(インライン)コールバック関数」のため、指定JSロード後に任意のJSコードを実行可能です。



var base = 'https://cdn.jsdelivr.net/npm/highlightjs@9.12.0';

Defer.css(base + '/styles/rainbow.css', 'hljs-css', 700);
Defer.js(base + '/highlight.pack.min.js', 'hljs-js', 700, function () {
  Defer.dom('pre code', 100, null, hljs.highlightBlock, {rootMargin: "120% 0%"});
});

 スクロールして対象要素が現れるたびにレンダリングする場合、Defer.dom() 関数の4つめの引数に「要素ごとに実行される関数名」を指定‼️
 05行め:「highlight.js」ライブラリの新しいバージョンでは、hljs.highlightBlock は非推奨になりました!
 代わりに、hljs.highlightElement を利用してください。

 画面表示領域にCSSセレクタ指定要素が現れるたびに要素をレンダリングする、Defer.dom() 関数 を実行。( JetThemeの場合、Defer.dom2() を利用 )



Defer.lazy 変数

  // defer.js ライブラリ関数利用コードの先頭で 記述!
  Defer.lazy = true;

 ユーザーが マウス操作などアクションを起こすまで 遅延ロードを保留すべく、以下4関数の最終パラメータ(waitForUserAction)の初期値を false から true にオーバーライド(上書き)します。

 つまり、以下4関数の最終パラメータ(waitForUserAction)を省略した場合、true とみなされます‼️

 Defer()Defer.css()Defer.js()Defer.all()


 (注)JetTheme の場合、jtCallback() 関数内の 先頭で 記述します!



Defer.all() と preload

 JetTheme で Defer.all() を利用したい場合、Defer.js ライブラリを  <head>  要素内 組み込み 推奨です。

 Defer.js()Defer.css() の2関数は、非同期で 実行されます‼️

 (グループごとに)同期して JSスクリプトの実行を行いたい場合は、Defer.all() 関数が用意されています。

 また、次で説明する「Google AdSense 広告」など <script> 記述のままで Lazy-Load対象にしたい場合も、Defer.all() 関数を利用します。

 Defer.js Version: 3.2.0 以降、この関数で指定したJSスクリプトには preload が自動適用されます‼️ 

 preload とは「リソース(プリロード)ヒント」とも呼ばれる機能で、「HTML解析中に、バックグラウンドでファイルをダウンロード希望」とブラウザに対してヒントを与えます。
 (ブラウザ判断ですが)バックグラウンドでファイルダウンロードされた場合、利用時のダウンロード時間が短縮されるため PSI 数値が良くなることが多いです。
 筆者の環境では、Prism.js用のCSS+JSファイルを preload しただけで、5ポイント以上アップしました。

 次の次で説明しますが、 手動でも preload を追加可能  です❗️


 ※ Defer.js() と Defer.css() の2関数は、非同期実行のため(インライン)コールバック関数を指定可能です。
 以下の例では、Defer.js() にて指定JSのロード後、Defer.dom() が実行されますので、依存関係がある場合は 必ず(インライン)コールバック関数を利用してください❗️

  Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
    Defer.dom('.twitter-tweet, .twitter-timeline', 500, null, function(element) {
      twttr.widgets.load(element);
    }, {rootMargin: "150% 0%"});
  });

Google AdSense 広告


 (注)今後は「AdSense広告コードの改変」とみなされる可能性もあるため、ご自身の判断のもとで行ってください‼️ 


 以下は、Google AdSense レスポンシブ広告のサンプルコード。
 以降、ca-pub-123456 の部分の数字は、ご自身の「サイト運営者ID」に置き換えてください。

<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456"
     crossorigin="anonymous"></script>
<!-- yyyymmdd_responsive_square -->
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-123456"
     data-ad-slot="1234567890"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>

 すべての「AdSense広告コード」から、先頭部分の
  <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456" crossorigin="anonymous"></script> 
 を削除します。


<script type="defer-adsense" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-123456" crossorigin="anonymous"></script>
<script>
  Defer.all('script[type="defer-adsense"]', 1000, true);
</script>

 ( 上記htmlコードは、 </body>  直前に挿入します )


1行め:削除したコードから asynctype="defer-adsense" (属性値は任意)に置き換え、この <script> 要素の実行を保留

3行め:<script> 記述のままで Lazy-Loadするには、「Defer.jsライブラリ」の Defer.all関数を利用
 その他にも、Defer.all関数は「JS同期実行」、Defer.js関数は「JS非同期実行」機能を提供の違いあり

 パラメータ1:CSSセレクタ で、「type="defer-adsense"」属性を付加した <script> 要素を指定
 パラメータ2:遅延時間をミリ秒(1000だと1秒)で指定。 省略値は ZERO
 パラメータ3:false だと遅延時間後に実行、true だと(マウス操作など)ユーザーがアクションを起こすまで、指定した <script> 要素の実行を保留。 省略値は false

 ※ 「type="deferjs"」属性を指定すれば、 Defer.all関数の呼び出しも省略可能。
Defer.jsライブラリは type="deferjs" 属性を付加した <script> 要素を順番に(同期実行で)、Defer.all関数を 省略値のままで 自動的に呼び出す仕様です。
 ( パラメータ1の省略値は、type="deferjs"
 事前に Defer.lazy = true; を記述しておけば、「パラメータ3の省略値」を true に置き換え可能。


 筆者が利用している JetTheme では テンプレート(テーマ)内に最初からDefer.jsライブラリが組み込まれていて type="deferjs" がすでに利用されているためか、逆に PSIスコアが70代になってしまいました。
 専用の属性値( type="defer-adsense" )を利用して、PSIスコア90以上を確認できた 上記コードを掲載しています。


 (注)テストのため type="defer-adsense" 属性を利用しましたが、Google Blogger 用「JetTheme」テンプレート(テーマ)は XMLファイルの先頭部分で「AdSense サイト運営者ID」「GA4 Analytics トラッキングコード」を指定するだけで、自動的に Lazy-Load 対象になります。


 【参考記事】




preload 手動追加

 Defer.all() 関数を利用しない場合でも、preload を手動で  <head>  要素内に追加すると PSI 数値が良くなる場合が多いです。

 ほとんど何でも preload 可能ですが、以下の例では ファイルサイズが大きめの Prism.js 用のCSSファイルとJSファイルを preload しています。

<head>
  <!-- 省略 -->
  <link as='style' href='https://past.gadgets-geek.net/css/prism.css' rel='preload'/>
  <link as='script' href='https://past.gadgets-geek.net/js/prism129.js' rel='preload'/>
  <!-- 省略 -->
</head>

 以下は、同サンプルコードの JetTheme(Google Blogger)版です。

<head>
  <!-- 省略 -->
  
  <!-- 投稿(記事)の場合のみ、Prism.js用 preload を実施 -->
  <b:if cond='data:view.isPost'>
    <link as='style' href='https://past.gadgets-geek.net/css/prism.css' rel='preload'/>
    <link as='script' href='https://past.gadgets-geek.net/js/prism129.js' rel='preload'/>
  </b:if>
  
  <!-- 省略 -->
</head>


Defer()

 JetThemeDefer() 関数を利用したい場合、Defer.js ライブラリを  <head>  要素内 組み込み してください。

 Defer() 関数を利用すると、JS関数単位で 遅延ロード可能です。

 サンプルコードを含め、詳細は こちら を参照ください。



Defer.reveal()

 Defer.reveal() 関数は、「Defer.js ライブラリ」によって遅延ロードされた Node(要素) をプログラムコードで、即時公開 するためのものです。

 サンプルコードを含め、詳細は こちら を参照ください。





JetTheme jtCallback() 例A

 Twitter や Instagram をページ内に多数埋め込む場合は、Defer.dom2() の遅延時間(ミリ秒)の調整が必要です‼️
 ツイート多数埋め込み対処のため、Defer.dom2() の遅延時間を「1000ms=1秒」に変更。
 (埋め込み数がそんなに多くなければ、500ms程度で良いかもしれません)

 Twitter と Instagram は API を直利用すれば細かく設定可能なため、(ただ埋め込んだだけでは)ページ内に埋め込んだ「全ツイート」または「全インスタ」が一度にまとめてレンダリングされる仕様のようです。
 (少し面倒ですが)要素ごとに実行する関数名が判明している場合、スクロールして画面に現れるたびに「ロードしてレンダリング」も可能です。


 (注)JetTheme以外の場合は、Defer.dom2Defer.dom に置換必要‼️ )

function jtCallback() {

/*Your Script is here to maintain performance.*/

  try {
    const ele_body = document.getElementsByTagName('body')[0];						//bodyタグは1つだけ

    Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "100% 0%"});			//ネイティブLazy-Loadと混在OK
    Defer.dom2('.defer-iframe iframe', 100, null, null, {rootMargin: "100% 0%"});	//ネイティブLazy-Loadと混在OK

    // 投稿記事「一覧」表示の場合、jtCallback() の以下を実行しない
    if ( ele_body.classList.contains('is-single') || ele_body.classList.contains('is-page') ) {
      ;			//空文(以下を実行するため)
    } else {
      return;	//この関数を終了
    }

  } catch (error) {
    console.log(error);
  }

  try {
    const prismEmbed = document.querySelectorAll('[class*="language-"]');	// Prism.js ( class language- 部分一致 )
    const tw_EmbedTW = document.getElementsByClassName('twitter-tweet');	// Twitter TWeet    (original-css-class)
    const tw_EmbedTL = document.getElementsByClassName('twitter-timeline');	// Twitter TimeLine (original-css-class)
    const instaEmbed = document.getElementsByClassName('instagram-media');	// Instagram (original-css-class)
    const tiktokEmbed = document.getElementsByClassName('tiktok-embed');	// TikTok

    if (prismEmbed.length !== 0) {
      window.Prism = window.Prism || {};	// グローバル変数 window.Prismオブジェクトを初期化
      Prism.manual = true;					// 全Code要素を自動的にハイライトしないモードに変更
      Defer.css('https://past.gadgets-geek.net/css/prism.css', 'prism-css', 700);
      Defer.js('https://past.gadgets-geek.net/js/prism129.js', 'prism-js', 700, function() {
        Defer.dom2('pre code', 100, null, Prism.highlightElement, {rootMargin: "100% 0%"});
      });
    }

    if (tw_EmbedTW.length !== 0 || tw_EmbedTL.length !== 0) {
      // class名を変更しない場合(一括レンダリング)
      Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
      	// 動的生成のdiv要素(iframe要素を含む)が対象となるよう、遅延時間を調整 500〜
        Defer.dom2('.twitter-tweet, .twitter-timeline', 1000, null, twttr.widgets.load, {rootMargin: "150% 0%"});
      });
    }

    if (instaEmbed.length !== 0) {
      // class名を変更しない場合(一括レンダリング)
      Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, function() {
        Defer.dom2('.instagram-media', 100, null, instgrm.Embeds.process, {rootMargin: "150% 0%"});
      });
    }

    // 要素ごとに実行される関数名が不明のため、Defer.dom()は利用せず
    if (tiktokEmbed.length !== 0) Defer.js('https://www.tiktok.com/embed.js', 'tiktok-js', 1000);

  } catch (error) {
    console.log(error);
  }

}


JetTheme jtCallback() 例B

  スクロール時、 Twitter または Instagram 埋め込み要素が現れるたびに レンダリングするには… 

 Defer.js 開発者の Shin さんが、少しチートな対応方法 を教えてくれました‼️

 デフォルトで付与されるCSSクラスを付加した状態で TwitterInstagram指定JSを実行すると、ページ内の全該当要素が一度にレンダリングされる仕様(一括レンダリング)になっています。

 要素が現れるたびにレンダリングさせるには、通常 API利用推奨 のためなのかもしれません。


  JSコード説明だけで難しい方は、別記事として わかりやすく まとめましたので ご覧ください❗️
 ( 親要素の追加CSSクラス名の変更 を、JSコード利用で一括で行う 方法を追加しています )




Twitter要素が現れるたびにレンダリング


<div class="embed-twitter">

  <blockquote class="lazy-tweet"><p lang="ja" dir="ltr">ちなみに私は、<br>node.className = &#39;twitter-tweet tw-align-center&#39;<br>に置換させてます。<br><br>tw-align-centerはTweetをセンタリングさせるクラスですが、いつも手作業でこのクラスを追加していたのが、自動で置き換えてくれるので便利です。</p>&mdash; あトん🤖Web CodingとAV好きなエンジニア (@HeavyPeat) <a href="https://twitter.com/HeavyPeat/status/1630923475220320257?ref_src=twsrc%5Etfw">March 1, 2023</a></blockquote>

</div>

 下記コード14行め:node.parentNode親要素)を引数として関数を実行するため、上記のように「埋め込みコードを、div 要素で囲む」必要があります‼️

 複数箇所 埋め込む場合は、「class="embed-twitter"」を持つ(クラス名は任意)  div  要素で囲みます。(または、それぞれユニークな id を持つ  div  要素で囲みます)
  div 要素で適切に囲まないと、node.parentNode(親要素)が「記事本文」など 広範囲になってしまい、要素が現れるたびにレンダリングできません❗️

(注) Instagram を埋め込む場合も、「class="embed-insta"」を持つ(クラス名は任意)  div  要素で囲まないといけません。

 「指定JS実行」時の一括レンダリングを防ぐため、「埋め込みコード」内の CSSクラス名を "lazy-tweet" に変更しておきます。
(タイムラインの場合は、"lazy-timeline" に変更)

const tw_EmbedTW2 = document.getElementsByClassName('lazy-tweet');
const tw_EmbedTL2 = document.getElementsByClassName('lazy-timeline');
// 全、該当要素の class='twitter-tweet' を class='lazy-tweet' に変更後
// 全、該当要素の class='twitter-timeline' を class='lazy-timeline' に変更後
// CSS classは複数指定NG(オリジナルclass名に戻す際、複数指定OK)
if (tw_EmbedTW2.length !== 0 || tw_EmbedTL2.length !== 0) {
  Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
    Defer.dom('.lazy-tweet, .lazy-timeline', 0, null, function(node) {
      if (node.className === 'lazy-timeline') {
        node.className = 'twitter-timeline';
      } else {
        node.className = 'twitter-tweet';
      }
      twttr.widgets.load(node.parentNode);
    }, {rootMargin: "150% 0%"});
  });
}

  JS実行時に対象要素とならないよう、Twitter と Instagram 埋め込みコードでデフォルトで付与されるCSSクラスを変更 
  サンプルコードでは、"lazy-tweet" "lazy-timeline" "lazy-instagram" を利用

  Twitter と Instagram の個々の要素をレンダリングするたび、デフォルトで付与されるCSSクラスに戻してから 要素ごとに実行すべき関数を呼び出し


※ JSで、CSSクラス名を変更する場合(ユニークなidを持つ  div  要素で囲みます)
 2023/05/04 すべてのCSSクラス名が置換されない 不具合を修正するため、getElementsByClassName() から querySelectorAll() 利用に変更。
( 原因は、HTMLコレクションNodeリスト の違い

const tw_EmbedTW = document.querySelectorAll('.twitter-tweet');
const tw_EmbedTL = document.querySelectorAll('.twitter-timeline');
const instaEmbed = document.querySelectorAll('.instagram-media');

let num = 0;	// Twitter TWeet
tw_EmbedTW.forEach(function(node) {
  let p_node = document.createElement('div');
  p_node.id = ('tw-tw-' + ++num);
  node.parentNode.insertBefore(p_node, node);
  p_node.appendChild(node);
  node.classList.replace('twitter-tweet', 'lazy-tweet');
});

num = 0;		// Twitter TimeLine
tw_EmbedTL.forEach(function(node) {
  let p_node = document.createElement('div');
  p_node.id = ('tw-tl-' + ++num);
  node.parentNode.insertBefore(p_node, node);
  p_node.appendChild(node);
  node.classList.replace('twitter-timeline', 'lazy-timeline');
});

num = 0;		// Instagram
instaEmbed.forEach(function(node) {
  let p_node = document.createElement('div');
  p_node.id = ('insta-' + ++num);
  node.parentNode.insertBefore(p_node, node);
  p_node.appendChild(node);
  node.classList.replace('instagram-media', 'lazy-instagram');
});

01行め:引数はCSSセレクタ文字列で、CSSクラス名「twitter-tweet」を持つ要素を抽出
07〜10行め:親Nodeとなる、(ユニークな id を持つ) div  要素を作成して追加
11行め:CSSクラス名「twitter-tweet」を「lazy-tweet」に置換




Instagram要素が現れるたびにレンダリング


 HTMLコード は、上記 Twitterの場合 を参照ください。

 <blockquote> タグのCSSクラス名を "lazy-instagram" に変更後、
class="embed-insta"」を持つ(クラス名は任意)  div  要素で囲みます❗️
(または、それぞれユニークな id を持つ  div  要素で囲みます)

const instaEmbed2 = document.getElementsByClassName('lazy-instagram');
// 全、該当要素の class='instagram-media' を class='lazy-instagram' に変更後
// CSS classは複数指定NG(オリジナルclass名に戻す際、複数指定OK)
if (instaEmbed2.length !== 0) {
  Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, function() {
    Defer.dom('.lazy-instagram', 0, null, function(node) {
      node.className = 'instagram-media';
      instgrm.Embeds.process(node.parentNode);
    }, {rootMargin: "150% 0%"});
  });
}


 例A(一括レンダリング)例B(スクロールするたびにレンダリング) の 両対応コード も掲載‼️ 


 オリジナルのクラス名に戻すときは 複数クラス指定しても問題無いため、あトん さんは node.className = 'twitter-tweet tw-align-center';
として、センタリング用のクラスを追加してるそうですよ。



 (注)JetTheme以外の場合は、Defer.dom2Defer.dom に置換必要‼️ )

function jtCallback() {

/*Your Script is here to maintain performance.*/

  try {
    const ele_body = document.getElementsByTagName('body')[0];					//bodyタグは1つだけ

    Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "100% 0%"});		//ネイティブLazy-Loadと混在OK
    Defer.dom2('.defer-iframe iframe', 100, null, null, {rootMargin: "100% 0%"});//ネイティブLazy-Loadと混在OK

    // 投稿記事「一覧」表示の場合、jtCallback() の以下を実行しない
    if ( ele_body.classList.contains('is-single') || ele_body.classList.contains('is-page') ) {
      ;			//空文(以下を実行するため)
    } else {
      return;	//この関数を終了
    }

  } catch (error) {
    console.log(error);
  }

  try {
    const tw_EmbedTW = document.querySelectorAll('.twitter-tweet');
    const tw_EmbedTL = document.querySelectorAll('.twitter-timeline');
    const instaEmbed = document.querySelectorAll('.instagram-media');

    let num = 0;	// Twitter TWeet
    tw_EmbedTW.forEach(function(node) {
      let p_node = document.createElement('div');
      p_node.id = ('tw-tw-' + ++num);
      node.parentNode.insertBefore(p_node, node);
      p_node.appendChild(node);
      node.classList.replace('twitter-tweet', 'lazy-tweet');
    });

    num = 0;		// Twitter TimeLine
    tw_EmbedTL.forEach(function(node) {
      let p_node = document.createElement('div');
      p_node.id = ('tw-tl-' + ++num);
      node.parentNode.insertBefore(p_node, node);
      p_node.appendChild(node);
      node.classList.replace('twitter-timeline', 'lazy-timeline');
    });

    num = 0;		// Instagram
    instaEmbed.forEach(function(node) {
      let p_node = document.createElement('div');
      p_node.id = ('insta-' + ++num);
      node.parentNode.insertBefore(p_node, node);
      p_node.appendChild(node);
      node.classList.replace('instagram-media', 'lazy-instagram');
    });

    const prismEmbed = document.querySelectorAll('[class*="language-"]');	// Prism.js ( class language- 部分一致 )
    const tw_EmbedTW2 = document.getElementsByClassName('lazy-tweet');	// ChangedClass(Twitter TWeet)
    const tw_EmbedTL2 = document.getElementsByClassName('lazy-timeline');	// ChangedClass(Twitter TimeLine)
    const instaEmbed2 = document.getElementsByClassName('lazy-instagram');// ChangedClass(Instagram)
    const tiktokEmbed = document.getElementsByClassName('tiktok-embed');	// TikTok

    if (prismEmbed.length !== 0) {
      window.Prism = window.Prism || {};	// グローバル変数 window.Prismオブジェクトを初期化
      Prism.manual = true;					// 全Code要素を自動的にハイライトしないモードに変更
      Defer.css('https://past.gadgets-geek.net/css/prism.css', 'prism-css', 700);
      Defer.js('https://past.gadgets-geek.net/js/prism129.js', 'prism-js', 700, function() {
        Defer.dom2('pre code', 100, null, Prism.highlightElement, {rootMargin: "100% 0%"});
      });
    }

    if (tw_EmbedTW2.length !== 0 || tw_EmbedTL2.length !== 0) {
      // 全、該当要素の class='twitter-tweet' を class='lazy-tweet' に変更後
      // 全、該当要素の class='twitter-timeline' を class='lazy-timeline' に変更後
      // CSS classは複数指定NG(オリジナルclass名に戻す際、複数指定OK)
      Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
        Defer.dom2('.lazy-tweet, .lazy-timeline', 0, null, function(node) {
          if (node.className === 'lazy-timeline') {	// class指定 ( lazy-tweet OR lazy-timeline )
            node.className = 'twitter-timeline';	// オリジナルclass名に戻す(複数class指定もOK)
          } else {
            node.className = 'twitter-tweet';		// オリジナルclass名に戻す(複数class指定もOK)
          }
          twttr.widgets.load(node.parentNode);		// 親Nodeに対し、要素ごとに実行すべき関数を呼出
        }, {rootMargin: "150% 0%"});
      });
    }

    if (instaEmbed2.length !== 0) {
      // 全、該当要素の class='instagram-media' を class='lazy-instagram' に変更後
      // CSS classは複数指定NG(オリジナルclass名に戻す際、複数指定OK)
      Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, function() {
        Defer.dom2('.lazy-instagram', 0, null, function(node) {
          node.className = 'instagram-media';		// オリジナルclass名に戻す(複数class指定もOK)
          instgrm.Embeds.process(node.parentNode);	// 親Nodeに対し、要素ごとに実行すべき関数を呼出
        }, {rootMargin: "150% 0%"});
      });
    }

    // 要素ごとに実行される関数名が不明のため、Defer.dom()は利用せず
    if (tiktokEmbed.length !== 0) Defer.js('https://www.tiktok.com/embed.js', 'tiktok-js', 1000);

  } catch (error) {
    console.log(error);
  }

}


JetTheme jtCallback() 例C

 例A(一括レンダリング)例B(スクロールするたびにレンダリング) の両対応コードも掲載‼️

  過去の全記事の「 Twitter + Instgram 」クラス名を変更するのが大変な場合を想定 

 Twitter や Instagram を たくさん埋め込んだ記事のみ、クラス名を変更すればOKです❗️

  Twitter / Instagram ごとで「オリジナルのクラス名」を優先するので、一つのページ(記事)内に「オリジナルのクラス名」と「変更後のクラス名」を混在してはいけません。
 ページ(記事)内に「オリジナルのクラス名」記述が存在しない場合、「変更後のクラス名を持つ要素」を探す実装です。
 ( Twitterのみ・Instagramのみ・TwitterとInstagram両方、3パターンのクラス名変更に対応 )


 ※ const 変数定義(定数という概念で、再代入を禁止)では、 三項演算子  を利用可能です。
 下記記事の通り、「アロー関数」や「インライン関数(無名関数)」を const 変数定義で利用すれば、複雑な  if  文や  switch case  文なども書けるようですね。

  const tw_EmbedTW = document.getElementsByClassName('twitter-tweet');		// Twitter TWeet    (original-css-class)
  const tw_EmbedTL = document.getElementsByClassName('twitter-timeline');	// Twitter TimeLine (original-css-class)
  const tw_EmbedTW2 = (tw_EmbedTW.length === 0) ? document.getElementsByClassName('lazy-tweet') : [] ;		// ChangedClass(TW)
  const tw_EmbedTL2 = (tw_EmbedTL.length === 0) ? document.getElementsByClassName('lazy-timeline') : [] ;	// ChangedClass(TL)
  const instaEmbed = document.getElementsByClassName('instagram-media');	// Instagram (original-css-class)
  const instaEmbed2 = (instaEmbed.length === 0) ? document.getElementsByClassName('lazy-instagram') : [] ;	// ChangedClass(Insta)



 (注)JetTheme以外の場合は、Defer.dom2Defer.dom に置換必要‼️ )

function jtCallback() {

/*Your Script is here to maintain performance.*/

  try {
    const ele_body = document.getElementsByTagName('body')[0];					//bodyタグは1つだけ

    Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "100% 0%"});		//ネイティブLazy-Loadと混在OK
    Defer.dom2('.defer-iframe iframe', 100, null, null, {rootMargin: "100% 0%"});//ネイティブLazy-Loadと混在OK

    // 投稿記事「一覧」表示の場合、jtCallback() の以下を実行しない
    if ( ele_body.classList.contains('is-single') || ele_body.classList.contains('is-page') ) {
      ;			//空文(以下を実行するため)
    } else {
      return;	//この関数を終了
    }

  } catch (error) {
    console.log(error);
  }

  try {
    const prismEmbed = document.querySelectorAll('[class*="language-"]');	// Prism.js ( class language- 部分一致 )
    const tw_EmbedTW = document.getElementsByClassName('twitter-tweet');	// Twitter TWeet    (original-css-class)
    const tw_EmbedTL = document.getElementsByClassName('twitter-timeline');	// Twitter TimeLine (original-css-class)
    const tw_EmbedTW2 = (tw_EmbedTW.length === 0) ? document.getElementsByClassName('lazy-tweet') : [] ;	// ChangedClass(TW)
    const tw_EmbedTL2 = (tw_EmbedTL.length === 0) ? document.getElementsByClassName('lazy-timeline') : [] ;	// ChangedClass(TL)
    const instaEmbed = document.getElementsByClassName('instagram-media');	// Instagram (original-css-class)
    const instaEmbed2 = (instaEmbed.length === 0) ? document.getElementsByClassName('lazy-instagram') : [] ;// ChangedClass(Insta)
    const tiktokEmbed = document.getElementsByClassName('tiktok-embed');	// TikTok

    if (prismEmbed.length !== 0) {
      window.Prism = window.Prism || {};	// グローバル変数 window.Prismオブジェクトを初期化
      Prism.manual = true;					// 全Code要素を自動的にハイライトしないモードに変更
      Defer.css('https://past.gadgets-geek.net/css/prism.css', 'prism-css', 700);
      Defer.js('https://past.gadgets-geek.net/js/prism129.js', 'prism-js', 700, function() {
        Defer.dom2('pre code', 100, null, Prism.highlightElement, {rootMargin: "120% 0%"});
      });
    }

    if (tw_EmbedTW2.length !== 0 || tw_EmbedTL2.length !== 0) {
      // 全、該当要素の class='twitter-tweet' を class='lazy-tweet' に変更後
      // 全、該当要素の class='twitter-timeline' を class='lazy-timeline' に変更後
      // CSS classは複数指定NG(オリジナルclass名に戻す際、複数指定OK)
      Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
        Defer.dom2('.lazy-tweet, .lazy-timeline', 100, null, function(node) {
          if (node.className === 'lazy-timeline') {	// class指定 ( lazy-tweet OR lazy-timeline )
            node.className = 'twitter-timeline';	// オリジナルclass名に戻す(複数class指定もOK)
          } else {
            node.className = 'twitter-tweet';		// オリジナルclass名に戻す(複数class指定もOK)
          }
          twttr.widgets.load(node.parentNode);		// 親Nodeに対し、要素ごとに実行すべき関数を呼出
        }, {rootMargin: "150% 0%"});
      });
    } else if (tw_EmbedTW.length !== 0 || tw_EmbedTL.length !== 0) {
      // class名を変更しない場合(一括レンダリング)
      Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
      	// 動的生成のdiv要素(iframe要素を含む)が対象となるよう、遅延時間を調整 500〜
        Defer.dom2('.twitter-tweet, .twitter-timeline', 1000, null, twttr.widgets.load, {rootMargin: "150% 0%"});
      });
    }

    if (instaEmbed2.length !== 0) {
      // 全、該当要素の class='instagram-media' を class='lazy-instagram' に変更後
      // CSS classは複数指定NG(オリジナルclass名に戻す際、複数指定OK)
      Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, function() {
        Defer.dom2('.lazy-instagram', 100, null, function(node) {
          node.className = 'instagram-media';		// オリジナルclass名に戻す(複数class指定もOK)
          instgrm.Embeds.process(node.parentNode);	// 親Nodeに対し、要素ごとに実行すべき関数を呼出
        }, {rootMargin: "150% 0%"});
      });
    } else if (instaEmbed.length !== 0) {
      // class名を変更しない場合(一括レンダリング)
      Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, function() {
        Defer.dom2('.instagram-media', 100, null, instgrm.Embeds.process, {rootMargin: "150% 0%"});
      });
    }

    // 要素ごとに実行される関数名が不明のため、Defer.dom()は利用せず
    if (tiktokEmbed.length !== 0) Defer.js('https://www.tiktok.com/embed.js', 'tiktok-js', 1000);

  } catch (error) {
    console.log(error);
  }

}



コラム

"ネイティブLazy-Load"と"JSライブラリ"併用がオススメ

 2023年2月末現在も ネイティブLazy-Load の対象は『(静的な)img ・ iframe 』要素のみのため、Twitter や Instagram TikTok などのSNS埋め込みや、ソースコード表示用JSライブラリ適用の <code> 要素 などを遅延ロードさせたい場合は、JSライブラリを利用 しなければいけません‼️

 ブラウザがJS無効」設定の場合は 当然JSライブラリ も動作せず 表示不具合が発生する可能性が高いため、『ネイティブLazy-Load と JSライブラリ の併用』をオススメします❗️

 別記事として まとめましたので、ぜひご覧ください。

 ネイティブLazy-Load」状態の投稿記事を、(ブラウザがJS有効状態のみ)「JSライブラリ」が適用になるよう メモリ内の DOM を書き換えるサンプルコードを掲載しています‼️ 

  JSライブラリ対応のHTMLコードを投稿記事内に書かなければ、「JSライブラリの乗り換え」「JSライブラリを廃止」する場合でも、投稿記事を書き換える必要はありません。



JetTheme jtCallback() 例D


  投稿記事は、「ネイティブLazy-Load」が適用されるよう 記述してください❗️

 以下は JetTheme テンプレート(テーマ)の場合のサンプルで、「JS有効時 Defer.js 適用」に書き換えます。
 (注)JetTheme以外の場合は、Defer.dom2Defer.dom に置換必要‼️ 

function jtCallback() {

/*Your Script is here to maintain performance.*/

  try {
    const nodes = document.querySelectorAll('.entry-text img[loading="lazy"], .entry-text 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
    });

    //ネイティブLazy-Loadの代用
    Defer.dom2('img.my-lazyload', 0, null, null, {rootMargin: "100% 0%"});
    Defer.dom2('iframe.my-lazyload', 0, null, null, {rootMargin: "100% 0%"})
    
  } catch (error) {
    console.log(error);
  }

  try {
    const ele_body = document.getElementsByTagName('body')[0];	//bodyタグは1つだけ

    // 投稿記事「一覧」表示の場合、jtCallback() の以下を実行しない
    if ( ele_body.classList.contains('is-single') || ele_body.classList.contains('is-page') ) {
      ;			//空文(以下を実行するため)
    } else {
      return;	//この関数を終了
    }

//  console.log(ele_body.classList);
  } catch (error) {
    console.log(error);
  }

  try {
    const prismEmbed = document.querySelectorAll('[class*="language-"]');	// Prism.js ( クラス language- 部分一致 )
    const tw_EmbedTW = document.getElementsByClassName('twitter-tweet');	// Twitter TWeet
    const tw_EmbedTL = document.getElementsByClassName('twitter-timeline');	// Twitter TimeLine
    const instaEmbed = document.getElementsByClassName('instagram-media');	// Instagram
    const tiktokEmbed = document.getElementsByClassName('tiktok-embed');	// TikTok

    if (prismEmbed.length !== 0) {
      window.Prism = window.Prism || {};	// 空のPrismオブジェクト(配列)をグローバルスコープに追加
      Prism.manual = true;					// すべてのコード要素を自動的にハイライトしないモードに変更
      Defer.css('https://past.gadgets-geek.net/css/prism.css', 'prism-css', 700);
      Defer.js('https://past.gadgets-geek.net/js/prism129.js', 'prism-js', 700, function() {
        Defer.dom2('pre code', 100, null, Prism.highlightElement, {rootMargin: "100% 0%"});
      });
    }

    if (tw_EmbedTW.length !== 0 || tw_EmbedTL.length !== 0) {
      Defer.js('https://platform.twitter.com/widgets.js', 'twitter-js', 700, function() {
        Defer.dom2('.twitter-tweet, .twitter-timeline', 1000, null, twttr.widgets.load, {rootMargin: "150% 0%"});
      }, true);
    }

    if (instaEmbed.length !== 0) {
      Defer.js('https://www.instagram.com/embed.js', 'insta-js', 1000, function() {
        Defer.dom2('.instagram-media', 100, null, instgrm.Embeds.process, {rootMargin: "150% 0%"});
      }, true);
    }

    if (tiktokEmbed.length !== 0) Defer.js('https://www.tiktok.com/embed.js', 'tiktok-js', 1000, null, true);

  } catch (error) {
    console.log(error);
  }

}

  このコードの詳細説明は、上記リンク記事を お読みください 

 loading="lazy" 属性を付加したすべての  img    iframe  要素を Defer.js 適用に書き換える場合は、6行めの CSSセレクタ指定を 以下のように変更してください。
 上記サンプルコードでは、JetTheme テンプレート(テーマ)投稿記事本文 に限定しています。

 'img[loading="lazy"], iframe[loading="lazy"]'




遅延ロード、ネイティブLazy-Load

 ページ内に(画像やSNSなど)プレーンテキスト情報以外を埋め込むば埋め込むほど、Google PageSpeed InsightsPSI と略)数値が悪化します。

 ページ内の全要素を表示するまで時間がかかるのが原因で、以下の方法で解決できます。

  1. 画面スクロール時、新しく現れる要素のみを表示
  2. ファーストビュー 表示以外の要素を、遅延表示

 ※ ファーストビュー:ブラウザーでWebサイトを表示したとき、スクロールせずに最初に見える範囲

 1. は、スクロールして画像などの要素が画面表示された時点(またはその直前)で そのデータだけを読み込んで表示する方法で、スクロールして要素が現れるまで ファイル(データ)の読み込みを保留します。

 2. 一度に全要素をレンダリングするため時間がかかるのであれば、少し時間が経ってから一部( ファーストビュー 以外)をレンダリングして表示すれば良いという考えに基づいています。


 この解決方法は、一般的に「 遅延ロード(Lazy-load)」と呼ばれています!


 遅延ロード を実現する方法は、以下の2つ!

  1. (昔ながらの)JSライブラリ組み込み
  2. ブラウザのネイティブLazy-Load

 1. 新たに JS(PHP)ライブラリなどをあなたのサイトに組み込む必要があり、機能はライブラリ次第

 2. ブラウザ機能のためサイトにライブラリを組み込む必要は無いが、 ブラウザ依存のためレンダリングタイミングを制御することはできず、2023年2月末現在 <img> <iframe> 2要素のみ対象が最大の欠点  です。

 WordPress の新しめのバージョンの場合、何もしなくても「ネイティブLazy-Load」が適用されます。( loading="lazy" 属性を自動追加。 ページ最初の両要素のみ、この属性を外してくれるそうです… )
 WordPressテーマによっては、「ネイティブLazy-Load」をもう少し細かく制御できるようですね。

 WordPress 以外のブログやホームページの場合も、手動で <img> <iframe> 2要素に loading="lazy"heightwidth の3属性を加えるだけ で済みます。

 ※ Internet Explorer を除くモダン・ブラウザの(最新)バージョンでは、「ネイティブLazy-Load」をデフォルトでサポートします。
 ただし、Safariブラウザの場合 バージョン16.3までは <img> のみデフォルト対応のため、<iframe> 要素対応は「Safariの設定変更」が必要 です。( iPhone、iPad、Mac すべて )


  2023年3月27日リリースされた Safari16.4 以降で、「ネイティブLazy-Load」が <img> に加え <iframe> デフォルト対応に‼️ 

iOS16.4(iPadOS16.4)以降 であれば、Safari16.4 以降を含みます

Mac の場合、(Safariのメニュー)[Safari] - [Safariについて] でSafariのバージョンを確認可能




 筆者も両要素の遅延ロードに関しては 2022年まで ブラウザの「ネイティブLazy-Load」を利用していましたが、2023年の投稿記事から「Defer.js」のみを利用しています‼️

 <img>・<iframe> 以外の要素を遅延ロード対象にする予定が無い方は「ネイティブLazy-Load」対応のみで良いと思いますが、Defer.js を併用してもパフォーマンスは落ちませんので  Twitter ・Instagram・TikTokなどSNSの埋め込み  に備えて導入しておくのもアリですよ。 細かく制御したい方も…



JetTheme用カスタマイズdiff

v2.5.0 左:オリジナル 右:JetTheme
 Defer.dom() JetThemeは 4つめの引数が追加され、引数6個

v3.4.0 左:オリジナル 右:JetTheme

v3.5.0 左:オリジナル 右:JetTheme

v3.6.0 左:オリジナル 右:JetTheme



Intersection Observer

 画像などの要素を 表示タイミングでレンダリングするため、
Defer.dom() 関数では JSの 交差オブザーバー API (Intersection Observer API) を利用しています。
 
(表示要素との)「 交差点 監視 API 」とでも訳せば良いのでしょうか?
 Internet Explorer などの古いブラウザでは実装されていないため、専用の polyfill を組み込まないと利用できません。

 rootMargin 属性を適切に設定すれば、要素が表示される直前から レンダリング可能です。




scriptタグ async, defer属性

 非同期(asynchronous)同期(synchronous) の意味は…
 Defer.all() 関数では、グループごとの JS 同期 実行機能を提供します。

 それに対し、Defer.js() 関数と Defer.css() 関数は 非同期( async )で 実行されます。(両関数とも、コールバック機能あり)


 HTML解析(HTMLパース)が中断されるため、極力  <head>  要素内で  <script>  タグを記述しない方が良い理由や、asyncdefer 属性の違いは…

 迷った場合は、defer を指定すれば良いはずです。
 両属性を指定しない場合、「HTML解析(HTMLパース)」が中断され、「JSのダウンロードと実行」が行われます。( PSI 数値が悪化する可能性あり )

 両属性とも「HTML解析(HTMLパース)」中に バックグラウンドで 非同期 でダウンロードされますが、 defer の場合  <script>  要素 呼び出し順で(同期して) JSが実行されます。
 async の場合 JSスクリプト実行も 非同期 のため、JS実行順は保証されません。

 defer の場合 「HTML解析(HTMLパース)」が完全に終わるまで JS実行を保留( 確実にDOM操作をすることができ、PSI 数値にも貢献 )するため、このライブラリを Defer.js と名付けたのでしょうね。

 【注意】src がない  <script>  タグの場合、defer 属性は無視されます






Macブログ ランキング アイコン
最後まで読んでいただき、ありがとうございます。 また、お越しくださいませ。
// アタル
For follow LINE Reader Group!Subscribe to this blog on Feedly!

Next Post Previous Post
No Comment
Add Comment
comment url