背景
普段の開発や検証作業で、DevToolsを開かずに任意のサイトへCSSを注入して見た目を変えることがよくあります。Stylebotのような拡張機能や、DevToolsの「Styles」パネルを直接編集する方法が定番です。
ただ、この手のツールを使うたびに気になっていたのが「スタイルを保存するたびに画面が一瞬チラつく」現象でした。原因を追っていくと、実装のほとんどが同じパターンで書かれていることに気づきました。
// ありがちな実装
function applyStyle(css) {
const old = document.getElementById('injected-style');
if (old) old.remove(); // ① 既存の<style>を削除
const style = document.createElement('style');
style.id = 'injected-style';
style.textContent = css;
document.head.appendChild(style); // ② 新しい<style>を追加
}
① で一瞬スタイルが消えたページがレンダリングされ、② で再度スタイルが当たる。この間にブラウザが再計算・再描画を挟むと、ユーザーの目には「チラッ」と見えてしまいます。CSSの変更が多いほど、これが積み重なって体感速度を落とします。
削除せずに書き換える
対策はシンプルで、既存の<style>要素を削除せず、中身だけを書き換えることです。
function applyStyle(css) {
let style = document.getElementById('injected-style');
if (!style) {
style = document.createElement('style');
style.id = 'injected-style';
document.head.appendChild(style);
}
style.textContent = css; // 削除せず中身だけ差し替え
}
textContentの更新はブラウザにとって「既存ルールの再計算」で済み、要素の削除・追加のようなDOMツリー変更を伴いません。これだけでチラつきがほぼ消えます。
複数のCSSプロパティを個別に触る場合は、CSSStyleSheet.insertRule / deleteRule で該当ルールだけを差し替える方法もありますが、動的に生成するCSS全体を扱うなら上記の方法で十分実用的です。
なぜこれが地味に厄介なのか
- ホットリロードのたびにチラつくと、細かい調整(色・余白・アニメーション)をテンポよく試せない
-
MutationObserverでDOMを監視している他のスクリプトがある場合、①の「削除」が余計なイベントを発火させることがある - SPAでルート遷移のたびに
<style>を作り直す実装だと、遷移のたびにチラつきが起きる
個人的な話
このパターンに何度もぶつかったので、CSSとJavaScriptを任意のサイトへ注入するChrome拡張機能「Njectify」を自分で作りました(Manifest V3)。上記のstyle書き換えはもちろん、:hoverや::beforeなどの疑似要素、レスポンシブのブレークポイントごとの編集にも対応しています。
JavaScript側は Monaco Editor(VS Codeと同じ編集エンジン)をベースにしたIDEを内蔵していて、ドメインごとにルールを作ってスクリプトを永続化できます。
Chrome Web Storeで公開しているので、興味があれば覗いてみてください。
- Chrome Web Store: https://chromewebstore.google.com/detail/njectify/eapjloogpkcdhpangehknokaljebcfgc
同じ問題に当たったことがある人の参考になれば幸いです。フィードバックがあればコメントで教えてください。
