概要
最近趣味で chrome 拡張機能を作っていましたが、その機能が主に「SPA 製のサイトで、要素を付け足したり、特定の要素の画像を差し替えること」で、特殊なテクニックが必要だったので tips として本記事に残します。
背景
ここは読み飛ばしても大丈夫です。
SPA とは
Single Page Application (シングルページアプリケーション) ですね。近年 Vue.js, React 等で主役になっている方式です。
誤解を恐れずに端的に言えば、「最初のロードを除いて、全体の再読み込みはせずに動的にDOMを書き換えて疑似的にページ遷移させる方式」というものです。
SPA サイトにおける chrome 拡張機能 (content_scripts)
chrome 拡張機能では、基本的に次のような流れで content_scripts (拡張機能の中で DOM などに触れるスクリプト) が実行されます。
- ブラウザが読み込まれた URL から、マッチする拡張機能の読み込みをさせる
- マッチした content_scripts を順次実行していく
即ち、ブラウザがロードされたタイミングでしか呼び出されないということです。
SPAはブラウザがロードされる (= 全体リフレッシュ) ことがないため、工夫しないとページの区別ができません。
Tips 集
SPA サイトのローディングを待つ
ここは SPA に限らずですが、近年の動的なサイト全般に利用できるテクニックです。
やることは単純で、「特定の要素が見つかるまで初期化処理を待つ」です。
function waitUntil(condition: () => boolean, callback: () => void, interval = 1000) {
let timer: NodeJS.Timeout | undefined = undefined;
const waiter = () => {
if (condition() && timer) {
clearInterval(timer);
callback();
}
};
timer = setInterval(waiter, interval);
}
waitUntil(
() => document.querySelector("SomeKeyElement") !== null,
() => {
// ロードが終わった後にしたいこと
main();
},
1000
);
SPA サイトでURLの変更を検知したい
汎用的な回答は難しいですが、MutationObserver
を用いて一定程度実現できます。
参考記事のように、body全体を補足してもいいですが、筆者が対象とするサイトでは title が変化していたので、 title のみ監視することで実現できました。
let currentPath = window.location.pathname;
// HACK: SPA の URL 変更を検知するために、title タグの変更を監視する
const title = document.getElementsByTagName("title")[0];
const observer = new MutationObserver(() => {
if (currentPath === window.location.pathname) return;
currentPath = window.location.pathname;
waitUntil(
() => document.querySelector("SomeKeyElement") !== null,
() => {
// ロードが終わった後にしたいこと
main();
},
1000
);
observer.observe(title, { childList: true });
上記のような方法で、URL の変更を監視できるので、URL 毎に異なる処理を行うこと容易になります。
参考