はじめに、自分の環境ではPageSpeed Insightsの「使用していないJavaScriptの削減」項目に対しては効果がありませんでした。
(スクリプト全体をsetTimeout
で遅らせると効果がでましたがあまりよろしくないような気がします)
addEventListener
をつかって実行したところPageSpeed Insightsの「使用していないJavaScriptの削減」の項目にwidgets.jsが出ないので効果はあるようです。
イベント後にJavaScriptを生成することで結果的に標準JS(widgets.jsなど)を遅延読み込みすることになるようです。
やってみたこと
- Bloggerが挿入する標準JSをコメントアウト
- コメントアウトした標準JSの内容をもとにJavaScriptを生成しbodyに挿入する
あくまでも実験的なものです。もし適用する場合は実験環境で行うか、必ずバックアップを取ってから作業してください。
使用方法
html
タグにb:js='false'
と設定されている場合はb:js='true'
に変更するかb:js='false'
を削除してください。(b:js='false'
と設定されていない場合はそのままでOKです)
以下のコードを</body>
と置き換えて使用します。
<!-- Bloggerが挿入するJavaScriptをコメントアウト -->
<div id='inserted-js-wrapper'>
<!--</div>
</body><div>--></div>
<script>
/*! Copyright:2023 sutajp | Released under the MIT license | https://sutasutashiki.blogspot.com/p/mit-license.html */
/**
* コメントアウトしたJavaScriptの内容をもとに
* JavaScriptを生成しbodyに挿入する関数
*/
//<![CDATA[
"use strict";
function insertJS() {
/**
* イベントを設定
* @type {Array<String>}
*/
const settingEvent = [
"touchstart",
"mousemove",
"scroll",
"click",
"keydown",
];
/** リスナーを設定
* @see {@link https://into-the-program.com/javascript-how-to-set-params-addeventlistener/}
* @type {Object} - createJS関数内の条件分岐でthisをつかうためhandleEventを使用
*/
const listener = { flag: true, handleEvent: createJS };
/** イベントリスナーを作成する関数
* @param {String} target - window
* @param {String} eventNames - 設定したイベント
* @param {createJS} listener - createJS
*/
function createEventListener(target, eventNames, listener) {
for (const event of eventNames) {
target.addEventListener(event, listener, { once: true });
}
}
/** イベントリスナーを削除する関数
* @param {String} target - window
* @param {String} eventNames - 設定したイベント
* @param {createJS} listener - createJS
*/
function removeMultipleEventListener(target, eventNames, listener) {
for (const event of eventNames) {
target.removeEventListener(event, listener);
}
}
/**
* コメントアウトしたJavaScriptの内容を抽出して
* 新しくJavaScriptをつくる関数
* @callback createJS
* @this {boolean}
*/
function createJS() {
/** 要素を取得 */
const wrapperElment = document.getElementById("inserted-js-wrapper");
/** HTMLに変換 */
const htmlElement = wrapperElment.innerHTML;
/**
* 外部スクリプト用正規表現
* @type {String} - "widgets/数字"を指定
*/
const externalRegexp = /widgets\/([0-9]*)/;
/**
* マッチした文字列を取得
* @type {Array<string|number>}
*/
const externalMatch = htmlElement.match(externalRegexp);
/** スクリプトタグ生成 */
const externalScript = document.createElement("script");
/** 外部スクリプトの数字部分にマッチした数字を入れる */
externalScript.src =
"https://www.blogger.com/static/v1/widgets/" +
externalMatch[1] +
"-widgets.js";
/** onloadイベントで外部スクリプトがloadした後にインラインスクリプトを生成 */
externalScript.onload = function () {
/**
* インラインスクリプト用正規表現
* @type {String}
*/
const inlineRegexp =
/(?<=<script type='text\/javascript'>)((\n|.)*?)(?=<\/script>)/;
/**
* マッチした文字列を取得
* @type {Array<String>}
*/
const inlineMatch = htmlElement.match(inlineRegexp);
/** スクリプトタグ生成 */
const inlineScript = document.createElement("script");
/** マッチした文字列をスクリプトタグの先頭に挿入 */
inlineScript.insertAdjacentHTML("afterbegin", inlineMatch[0]);
/** インラインスクリプトをbodyに挿入 */
document.body.appendChild(inlineScript);
};
/** 外部スクリプトをbodyに挿入 */
document.body.appendChild(externalScript);
/**
* createEventListener関数から実行された場合
* removeMultipleEventListenerを実行
*/
if (this.flag) {
removeMultipleEventListener(window, settingEvent, listener);
}
}
/** スクロールしていたらcreateJS関数を実行
* スクロールしていなかったらcreateEventListener関数を実行
*/
if (window.pageYOffset) {
/**
* @type {Object} - createJS関数の条件分岐でthisをつかうためオブジェクトを付けてcreateJS関数を実行
* @see {@link https://qiita.com/takkyun/items/c6e2f2cf25327299cf03#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89}
*/
const scrollingFlag = { flag: false };
scrollingFlag.createJS = createJS;
scrollingFlag.createJS();
} else {
createEventListener(window, settingEvent, listener);
}
}
window.addEventListener("load", insertJS, { once: true });
//]]>
</script>
</body>
<!-- End Bloggerが挿入するJavaScriptをコメントアウト -->
念のためスマートフォン用としてsettingEvent
に"touchstart"
を設定しましたが、タッチ操作はマウスのイベントであるclickでも検出できるそうなので不必要ならば"touchstart"
を削除してください。
const settingEvent = ["mousemove", "scroll", "click", "keydown"];
自分の環境では空のdiv
要素(<div id='inserted-js-wrapper'>
)はレイアウトに影響がありませんでした。
レイアウトに影響があったり気になる場合はdisplay:none;
を設定してください。
<div id='inserted-js-wrapper' style='display:none;'>
おわりに
コメントアウトした標準JSを<div>
などでラップ(包む)ことができるのを知ったのでつくってみました。
Bloggerから挿入されるJavaScriptを正規表現をつかって抽出しています。
したがって、挿入されるJavaScriptの構成(順番など)が変わると機能しなくなります。
また、試しにfetch
でwidgets.jsにアクセスしたところクロスオリジンの関係?で内容を得ることができませんでした。
コメントなし:
コメントを投稿