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

Defer.js は、Shin さんが作成したインラインでも わずか1.76KByteの「遅延ロード」JavaScriptライブラリの決定版‼️
Version: 3.4.0 以降のセットアップ( JetTheme はバージョンアップ )方法を解説します。
なお、WordPress 限定ですが、 お手軽な プラグイン 、 手動で制御されたい方向けの PHPライブラリ も Shin さんがリリースされています❗️
以降、 Defer.js JavaScript( JS )ライブラリ利用方法の説明となりますので、ご注意ください。
はじめに
Defer.jsを組み込むべきか?
遅延ロード 対象が <img> と <iframe> 2要素のみの場合、ブラウザの「 ネイティブLazy-Load 」で簡単に代用できるので、慌てて導入する必要はありません。
「ネイティブLazy-Load」利用時の注意点は、以下の2つ!
- PSI 数値が悪くなるので、ファーストビュー の上記2要素の loading="lazy" 属性は外す
- Safariブラウザの場合 <img> 要素のみデフォルト対応のため、 <iframe> 要素対応は Safariブラウザの設定変更 が必要
しかし、 Twitter・Instagram・TokTokなどの SNS や、 Prism.js・highlight.jsなどの ソースコード表示用ライブラリ をサイトのページに埋め込む予定がある方は、( ネイティブLazy-Load と混在してもまったく問題は無いため )Defer.js 組み込み をオススメします‼️
JS・CSSファイルだけではなくJS関数など ほとんど何でも遅延ロードでき、画面に表示される少し前から 遅延ロード させたり、遅延ロード時 要素ごとに(インライン)JS関数を実行 や、 遅延ロード後に CSSクラスを追加(つまり、CSS効果を遅延可能) するなどの オプション機能も用意されています。
多機能なうえ、インラインでサイト組み込み時 わずか 1.76KByte (約1,760半角文字)の超コンパクト実装も、他の「遅延ロードJSライブラリ」と比較したメリットの一つです。
Package @shinsenter/defer.js
Defer.js は、JavaScriptのマイクロライブラリで、(ほとんど)あらゆるものを遅延ロードさせることができます。
Defer.js は依存性ゼロ、超効率的、そして Webバイタル に貢献します‼️

Defer.js の全機能を知りたい方は 【公式】英語ドキュメント 、あるいは 日本語ドキュメント(筆者が翻訳)をお読みください。
現在のWeb技術では、「遅延ロード」と「リソース(プリロード)ヒント」の組み合わせが最も効果的な選択肢らしいです。
defer(遅延ロード)機能に加え、 Version: 3.2.0 以降 Defer.all() メソッドに リソース(プリロード)ヒント 機能が追加されました❗️
(JetTheme以外の)ブログ・ホームページへのセットアップ
WordPress 限定ですが、 お手軽な プラグイン 、 手動で制御されたい方向けの PHPライブラリ も Shin さんがリリースされています❗️
以降は、Defer.js JavaScript( JS )ライブラリの導入方法となります。
WordPress にて「 Defer.js の全機能と最新バージョン 」を利用したい方は、JavaScript つまり <script> 〜 </script> を適切に埋め込むため、以下の記事をオススメします。
とりあえず、JS を埋め込みたい方は…
JS埋め込み 4つの方法を解説(子テーマなど)…
条件分岐タグ利用で、JS埋め込み…
CDN利用のファイルロード
<head> 要素内の最初の方に、以下の1行を挿入すると、Defer.jsライブラリ(ファイル)がダウンロードされ 組み込まれます。
( 右上の「Copy」を押すと、コピーできます )
<script src="https://cdn.jsdelivr.net/npm/@shinsenter/defer.js@3.5.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.5.0/dist/defer.min.js"></script>
<!-- (省略) -->
</head>
<body> 要素の最初の方に組み込んでも動作しますが、Defer() ・ Defer.all() メソッド利用時に影響が出る場合があるため <head> 要素の最初の方で組み込むのかと思います。
Defer.jsライブラリに含まれる関数を利用するまでに、「Defer.jsライブラリのダウンロードと実行」が完了している必要がありますので…
Defer.js の新しいバージョンがリリースされた場合は、 @3.5.0 の数字の部分を修正するだけで大丈夫です❗️
<head> 要素内への JS組み込みは ページ内で最初に実行されますが、「(その間)他のHTML要素などの読み込みや解析が遅れる可能性」があります。
わずか1.76KByteに最適化済みですが、次で説明する インライン組み込み を推奨します‼️
少なくとも、( Defer.js ライブラリのダウンロードを行わず )HTTPリクエストを節約できますので…
インライン組み込み
Google Blogger以外のブログや、ホームページ
(ライブラリファイルのダウンロードを毎回行わず)HTTPリクエストを節約して、defer.min.js から全テキストをコピーして、以下7行めの「script要素内のコメント( /*全テキストで置換*/ )」と置き換えることで、Defer.jsライブラリ全体をインライン化することも可能です。
(とてもコンパクトなファイルサイズ 1.76KByte に最適化済み)
※上記は v3.5.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.5.0 版コードを JetTheme 用にカスタマイズしたもの。(2.07KByte)
6個の引数を持つ f.dom=function(n,t,c,i,r,u)
5個の引数を持つ f.dom2=function(n,t,c,i,r)
の 2関数 が見て取れますね。
/*!@shinsenter/defer.js@3.5.0*/
!function(t,s){function e(n){t.addEventListener(n,N)}function o(n){t.removeEventListener(n,N)}function f(n,t,e){k?A(n,t):((e===s?f.lazy:e)?L:C).push(n,t)}function i(n){x.head.appendChild(n)}function c(n,t){j.call(n.attributes)[g](function(n){t(n.name,n.value)})}function r(n,t,e,o){return o=t&&x.getElementById(t)||x.createElement(n),t&&(o.id=t),e&&(o.onload=e),o}function a(n,t){return j.call((t||x).querySelectorAll(n))}function l(o,n){a("source,img",o)[g](l),c(o,function(n,t,e){(e=/^data-(.+)/.exec(n))&&o[b](e[1],t)}),"string"==typeof n&&n&&(o.className+=" "+n),h in o&&o[h]()}function n(n,t,e){f(function(o){(o=a(n||"script[type=deferjs]"))[g](function(n,e){n.src&&(e=r(u),c(n,function(n,t){n!=E&&e[b]("src"==n?"href":n,t)}),e.rel="preload",e.as=p,i(e))}),function n(t,e){(t=o[w]())&&(e=r(p),c(t,function(n,t){n!=E&&e[b](n,t)}),e.text=t.text,t.parentNode.replaceChild(e,t),e.src&&!e.getAttribute("async")?e.onload=e.onerror=n:n())}()},t,e)}var d="Defer",u="link",p="script",h="load",m="pageshow",v=["touchstart","keydown","mousemove","wheel"],y="on"+m in t?m:h,g="forEach",b="setAttribute",w="shift",E="type",I=t.IntersectionObserver,x=t.document||t,A=t.setTimeout,j=v.slice,k=/p/.test(x.readyState),C=[],L=[],N=function(n,t){for(t=n.type==y?(o(y),k=f,v[g](e),C):(v[g](o),L);t[0];)A(t[w](),t[w]())};n(),f.all=n,f.dom=function(n,t,c,i,r,u){f(function(e){function o(n){r&&!1===r(n)||l(n,c)}e=I?new I(function(n){n[g](function(n,t){n.isIntersecting&&(t=n.target)&&(i&&i(t),e.unobserve(t),o(t))})},u):s,a(n||"[data-src]")[g](function(n){n[d]!=f&&(n[d]=f,e?e.observe(n):o(n))})},t,!1)},f.dom2=function(n,t,c,i,r){f(function(e){function o(n){i&&!1===i(n)||l(n,c)}e=I?new I(function(n){n[g](function(n,t){n.isIntersecting&&(e.unobserve(t=n.target),o(t))})},r):s,a(n||"[data-src]")[g](function(n){n[d]!=f&&(n[d]=f,e?e.observe(n):o(n))})},t,!1)},f.css=function(t,e,n,o,c){f(function(n){(n=r(u,e,o)).rel="stylesheet",n.href=t,i(n)},n,c)},f.js=function(t,e,n,o,c){f(function(n){(n=r(p,e,o)).src=t,i(n)},n,c)},f.reveal=l,t[d]=f,k||e(y)}(this);

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

JetTheme用 defer.js@3.5.0 と defer.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 は、「画面に表示される少し前からロード」や「要素(グループ)ごとの遅延時間の変更」など細かい調整が可能です❗️
- src → data-src
- 領域確保用の仮画像を src に設定
- 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 が付き、その分手前から レンダリングが開始されます。( margin 同様、"100% 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/AVvXsEiH0qoftwiekn-YGWjDGk210qDFUSOLkx0XE1c0Q5gplFrMyVU35bbZzxtRkwMl25ALUgxeB1WV_H5r0swH5lsngcygBSj7AuCuur_Hxrl3Zxu2Ia21UDNkjcdQoPoPv75NnQKCmVnj_0T378YqPwOzl8OxdO3IWnJW2NYAu1O38dSbE6rq16N3dBsTuw/s0-rw/defer350_0_origin.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/AVvXsEiH0qoftwiekn-YGWjDGk210qDFUSOLkx0XE1c0Q5gplFrMyVU35bbZzxtRkwMl25ALUgxeB1WV_H5r0swH5lsngcygBSj7AuCuur_Hxrl3Zxu2Ia21UDNkjcdQoPoPv75NnQKCmVnj_0T378YqPwOzl8OxdO3IWnJW2NYAu1O38dSbE6rq16N3dBsTuw/s0-rw/defer350_0_origin.webp" title="Defer.js ロゴ画像" width="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiH0qoftwiekn-YGWjDGk210qDFUSOLkx0XE1c0Q5gplFrMyVU35bbZzxtRkwMl25ALUgxeB1WV_H5r0swH5lsngcygBSj7AuCuur_Hxrl3Zxu2Ia21UDNkjcdQoPoPv75NnQKCmVnj_0T378YqPwOzl8OxdO3IWnJW2NYAu1O38dSbE6rq16N3dBsTuw/s0-rj-l10/defer350_0_origin.webp" />
</div>
JetTheme では、XMLファイル内 jtCallback() 関数内に「Defer.js関連のJSコード」を記述します❗️
以下画像だと、2482行めの箇所に、JSコードをどんどん追加。
(注意)v3.4.0 以降の「JetTheme版 Defer.js コード」を組み込んだ方は、引数5個のオリジナルの Defer.dom() 関数を利用したい場合、以下のように関数名を Defer.dom2 に変更しなければいけません‼️

Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "150% 0"});
iframe 要素
先に、「img 要素」の説明 をお読みください。
ネイティブLazy-Load と比べ Defer.js は、「画面に表示される少し前からロード」や「要素(グループ)ごとの遅延時間の変更」など細かい調整が可能です❗️
- src → data-src
- src="about:blank" を追加
- 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"});
ツイート埋め込みコードの 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"});
インスタ埋め込みコードの 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 は、「画面に表示される少し前からロード」や「要素(グループ)ごとの遅延時間の変更」など細かい調整が可能です❗️
- src → data-src style → data-style
- src="about:blank" を追加
- CSSセレクタで抽出できるよう、CSSクラス や id を追加
<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() を利用 )
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() 関数が用意されています。
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"});
});
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()
JetTheme で Defer() 関数を利用したい場合、Defer.js ライブラリを <head> 要素内 組み込み してください。
Defer() 関数を利用すると、JS関数単位で 遅延ロード可能です。
サンプルコードを含め、詳細は こちら を参照ください。
Defer.reveal()
Defer.reveal() 関数は、「Defer.js ライブラリ」によって遅延ロードされた Node(要素) をプログラムコードで、即時公開 するためのものです。
サンプルコードを含め、詳細は こちら を参照ください。
一部のスライドショーライブラリは、正確な画像比率をレンダリングするため、事前に正確な画像サイズを取得する必要があります。
— Shin (@shinsenter) March 17, 2023
JetTheme jtCallback() 例A
Twitter や Instagram をページ内に多数埋め込む場合は、Defer.dom2() の遅延時間(ミリ秒)の調整が必要です‼️
ツイート多数埋め込み対処のため、Defer.dom2() の遅延時間を「1000ms=1秒」に変更。
(埋め込み数がそんなに多くなければ、500ms程度で良いかもしれません)
Twitter と Instagram は API を直利用すれば細かく設定可能なため、(ただ埋め込んだだけでは)ページ内に埋め込んだ「全ツイート」または「全インスタ」が一度にまとめてレンダリングされる仕様のようです。
(少し面倒ですが)要素ごとに実行する関数名が判明している場合、スクロールして画面に現れるたびに「ロードしてレンダリング」も可能です。
(注)JetTheme以外の場合は、Defer.dom2 を Defer.dom に置換必要‼️ )
function jtCallback() {
/*Your Script is here to maintain performance.*/
try {
const ele_body = document.getElementsByTagName('body')[0]; //bodyタグは1つだけ
if (ele_body.classList.contains('is-single')) {
Defer.css('https://past.gadgets-geek.net/css/jt_post_min.css', 'post-css', 300); //投稿CSS
} else if (ele_body.classList.contains('is-home')) {
Defer.css('https://past.gadgets-geek.net/css/jt_home_min.css', 'home-css', 300); //投稿一覧CSS
} else if (ele_body.classList.contains('is-page')) {
Defer.css('https://past.gadgets-geek.net/css/jt_page_min.css', 'page-css', 300); //ページCSS
} else {
Defer.css('https://past.gadgets-geek.net/css/jt_home_min.css', 'home-css', 300); //投稿一覧CSS (is-search OR is-archive OR etc)
}
Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "150% 0"}); //ネイティブLazy-Loadと混在OK
Defer.dom2('.defer-iframe iframe', 100, null, null, {rootMargin: "150% 0"}); //ネイティブLazy-Loadと混在OK
if ( ele_body.classList.contains('is-single') || ele_body.classList.contains('is-page') ) {
; //空文(以下を実行するため)
} else {
return; //この関数を終了
}
} catch (error) {
console.log(error);
}
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
try {
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_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クラスを付加した状態で Twitter と Instagram の指定JSを実行すると、該当の全要素が一度にレンダリングされる仕様のため。
(要素ごとのレンダリングは、通常 API 利用かと思われます)
【 Twitter 】
<blockquote class="lazy-tweet"><p lang="ja" dir="ltr">ちなみに私は、<br>node.className = 'twitter-tweet tw-align-center'<br>に置換させてます。<br><br>tw-align-centerはTweetをセンタリングさせるクラスですが、いつも手作業でこのクラスを追加していたのが、自動で置き換えてくれるので便利です。</p>— あトん🤖Web CodingとAV好きなエンジニア (@HeavyPeat) <a href="https://twitter.com/HeavyPeat/status/1630923475220320257?ref_src=twsrc%5Etfw">March 1, 2023</a></blockquote>
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.dom2('.lazy-tweet, .lazy-timeline', 100, 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クラス名を変更する場合
const tw_EmbedTW = document.getElementsByClassName('twitter-tweet');
const tw_EmbedTL = document.getElementsByClassName('twitter-timeline');
const instaEmbed = document.getElementsByClassName('instagram-media');
for ( let n = 0 ; n < tw_EmbedTW.length ; n++ ) { // Twitter TWeet
tw_EmbedTW[n].classList.replace('twitter-tweet', 'lazy-tweet');
}
for ( let n = 0 ; n < tw_EmbedTL.length ; n++ ) { // Twitter TimeLine
tw_EmbedTL[n].classList.replace('twitter-timeline', 'lazy-timeline');
}
for ( let n = 0 ; n < instaEmbed.length ; n++ ) { // Instagram
instaEmbed[n].classList.replace('instagram-media', 'lazy-instagram');
}
【 Instagram 】(HTMLコードは省略)
<blockquote> タグ クラス名変更後
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.dom2('.lazy-instagram', 100, null, function(node) {
node.className = 'instagram-media';
instgrm.Embeds.process(node.parentNode);
}, {rootMargin: "150% 0"});
});
}
例A(一括レンダリング) と 例B(スクロールするたびにレンダリング) の 両対応コード も掲載‼️
オリジナルのクラス名に戻すときは 複数クラス指定しても問題無いため、あトん さんは
node.className = 'twitter-tweet tw-align-center'
として、センタリング用のクラスを追加してるそうですよ。
JSコード説明だけでは理解できない方は、りも さんが以下の記事としてまとめていますので、お読みください。
(注)JetTheme以外の場合は、Defer.dom2 を Defer.dom に置換必要‼️ )
function jtCallback() {
/*Your Script is here to maintain performance.*/
try {
const ele_body = document.getElementsByTagName('body')[0]; //bodyタグは1つだけ
if (ele_body.classList.contains('is-single')) {
Defer.css('https://past.gadgets-geek.net/css/jt_post_min.css', 'post-css', 300); //投稿CSS
} else if (ele_body.classList.contains('is-home')) {
Defer.css('https://past.gadgets-geek.net/css/jt_home_min.css', 'home-css', 300); //投稿一覧CSS
} else if (ele_body.classList.contains('is-page')) {
Defer.css('https://past.gadgets-geek.net/css/jt_page_min.css', 'page-css', 300); //ページCSS
} else {
Defer.css('https://past.gadgets-geek.net/css/jt_home_min.css', 'home-css', 300); //投稿一覧CSS (is-search OR is-archive OR etc)
}
Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "150% 0"}); //ネイティブLazy-Loadと混在OK
Defer.dom2('.defer-iframe iframe', 100, null, null, {rootMargin: "150% 0"});//ネイティブLazy-Loadと混在OK
if ( ele_body.classList.contains('is-single') || ele_body.classList.contains('is-page') ) {
; //空文(以下を実行するため)
} else {
return; //この関数を終了
}
} catch (error) {
console.log(error);
}
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
try {
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"});
});
}
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"});
});
}
// 要素ごとに実行される関数名が不明のため、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.dom2 を Defer.dom に置換必要‼️ )
function jtCallback() {
/*Your Script is here to maintain performance.*/
try {
const ele_body = document.getElementsByTagName('body')[0]; //bodyタグは1つだけ
if (ele_body.classList.contains('is-single')) {
Defer.css('https://past.gadgets-geek.net/css/jt_post_min.css', 'post-css', 300); //投稿CSS
} else if (ele_body.classList.contains('is-home')) {
Defer.css('https://past.gadgets-geek.net/css/jt_home_min.css', 'home-css', 300); //投稿一覧CSS
} else if (ele_body.classList.contains('is-page')) {
Defer.css('https://past.gadgets-geek.net/css/jt_page_min.css', 'page-css', 300); //ページCSS
} else {
Defer.css('https://past.gadgets-geek.net/css/jt_home_min.css', 'home-css', 300); //投稿一覧CSS (is-search OR is-archive OR etc)
}
Defer.dom2('.defer-img img', 100, null, null, {rootMargin: "150% 0"}); //ネイティブLazy-Loadと混在OK
Defer.dom2('.defer-iframe iframe', 100, null, null, {rootMargin: "150% 0"}); //ネイティブLazy-Loadと混在OK
if ( ele_body.classList.contains('is-single') || ele_body.classList.contains('is-page') ) {
; //空文(以下を実行するため)
} else {
return; //この関数を終了
}
} catch (error) {
console.log(error);
}
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
try {
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ライブラリを廃止」する場合でも、投稿記事を書き換える必要はありません。
遅延ロード、ネイティブLazy-Load
ページ内に(画像やSNSなど)プレーンテキスト情報以外を埋め込むば埋め込むほど、Google PageSpeed Insights( PSI と略)数値が悪化します。
ページ内の全要素を表示するまで時間がかかるのが原因で、以下の方法で解決できます。
- 画面スクロール時、新しく現れる要素のみを表示
- ファーストビュー 表示以外の要素を、遅延表示
※ ファーストビュー:ブラウザーでWebサイトを表示したとき、スクロールせずに最初に見える範囲
1. は、スクロールして画像などの要素が画面表示された時点(またはその直前)で そのデータだけを読み込んで表示する方法で、スクロールして要素が現れるまで ファイル(データ)の読み込みを保留します。
2. 一度に全要素をレンダリングするため時間がかかるのであれば、少し時間が経ってから一部( ファーストビュー 以外)をレンダリングして表示すれば良いという考えに基づいています。
この解決方法は、一般的に「 遅延ロード(Lazy-load)」と呼ばれています!
遅延ロード を実現する方法は、以下の2つ!
- (昔ながらの)JSライブラリ組み込み
- ブラウザの「ネイティブ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" + height + width の3属性を加えるだけ で済みます。
※ Internet Explorer を除くモダン・ブラウザの(最新)バージョンでは、「ネイティブLazy-Load」をデフォルトでサポートします。
ただし、Safariブラウザの場合 <img> のみデフォルト対応のため、Safariのバージョンが上がるたびに <iframe> 要素対応は「Safariの設定変更」が必要 です。( iPhone、iPad、Mac すべて )
筆者も両要素の遅延ロードに関しては 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
Intersection Observer
画像などの要素を 表示タイミングでレンダリングするため、
Defer.dom() 関数では JSの 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> タグを記述しない方が良い理由や、async と defer 属性の違いは…
迷った場合は、defer を指定すれば良いはずです。
両属性を指定しない場合、「HTML解析(HTMLパース)」が中断され、「JSのダウンロードと実行」が行われます。( PSI 数値が悪化する可能性あり )
両属性とも「HTML解析(HTMLパース)」中に バックグラウンドで 非同期 でダウンロードされますが、 defer の場合 <script> 要素 呼び出し順で(同期して) JSが実行されます。
async の場合 JSスクリプト実行も 非同期 のため、JS実行順は保証されません。
defer の場合 「HTML解析(HTMLパース)」が完全に終わるまで JS実行を保留( 確実にDOM操作をすることができ、PSI 数値にも貢献 )するため、このライブラリを Defer.js と名付けたのでしょうね。
【注意】src がない <script> タグの場合、defer 属性は無視されます
最後まで読んでいただき、ありがとうございます。 また、お越しくださいませ。
// アタル