【chrome拡張機能】コンテンツスクリプトについて
コンテンツスクリプトとは
コンテントスクリプトは表示しているサイトに自分が用意したCSSファイルやJavaScriptファイルを挿入することができる機能です。コンテンツスクリプトを使うことで閲覧中のサイトのDOMを自由に編集することができ、普段利用しているサイトを自分好みにカスタマイズすることができます。あるサイトにこれがあれば便利だなと思う機能があっても、運用会社が実装するまで待つ必要があったり、そもそも運用方針と違えば実装されることなどありません。しかしコンテンツスクリプトを使うことで自由にサイトをカスタマイズすることができるので、サイトの使い勝手がよくなります。例えばコンテントスクリプトを使えば次のようなことができるようになります。
- ダークモードに対応してないサイトでも背景を暗くする
- ブロック機能がついてないサイトでも特定のユーザーやコンテンツを非表示にできる
- Twitterに画像のダウンロードボタンを追加する
- 動画サイトで16倍速再生ができるようになる
コンテンツスクリプトでできることをまとめるなら
- 任意のCSS/JSファイルをサイトに挿入できる
- サイト上のDOMと通信したり編集すること
と言えます。実際、上に書いた例はCSS/JSで実装できる機能ばかりです。
拡張機能を作る前に、まずは「検証」を使ってブラウザのDOMにアクセスしましょう。JSを使うことでどれだけのことができるのかブラウザの「検証」を使ってコンテンツスクリプトの便利さを実感してみます。「検証」でできることはコンテンツスクリプトと比べて限られています。しかし「検証」でJavaScriptを実行するとリアルタイムでサイト上に反映されますし、「検証」でできることは大体コンテンツスクリプトでできます。このことから拡張機能の開発する際はいきなりコードに書かず、まずは「検証」で動作確認をすることが多いです。
このサンプルではYoutubeで実験をするので、Youtubeのお好きな動画を開いてみてください。特にこだわりがなければを開いてみてください。
動画を開いた画面上で右クリックをし「検証」メニューをクリックします。次に、出てきた検証ウィンドウの「Console」もしくは「コンソール」と書かれたタブをクリックしてください。するとコンソールパネルが開かれます。コンソールパネルではJavaScriptが実行でき、表示しているサイトのDOMにアクセスすることもできます。まずは以下のコード貼り付け、コンソールの一番下のエディタに貼り付け、Enterを押してください。すると動画が16倍再生されることはわかります。
document.querySelector('video').playbackRate = 16;
ここでやっていることは単にDOMの編集です。video
要素のplaybackRate
プロパティの値を16
に変更したとも言えます。動画の再生速度を変えるのは運営しかできないことのように思えますが、標準で提供されている機能の1つなので<video>
タグを使っているサイトなら使える機能です。次は低評価ボタンを非表示にしてみましょう。以下のコードを入力し、実行してください。
document.getElementById("segmented-dislike-button").style.display = "none"
すると低評価ボタンが消えました。CSSでDOMの表示形式を設定するdisplay
というプロパティの値をnone
に変更したので、低評価ボタンを作るDOMが非表示になりました。やはりここでもDOMを編集しただけということに注意してください。コンテンツスクリプトはサイト上のDOMを編集したり通信する機能だからです。またコンソールパネルではJSしか実行できないので、CSSの値を編集するためにJS経由で行いました。次はDOMを追加してみましょう。Youtubeのタイトルに別の文字を追加してみます。以下のコードを入力し、実行してみてください。
let area = document.getElementsByClassName("style-scope ytd-watch-metadata");
let h1 = document.createElement('h1');
h1.innerText = "Hello Youtube";
area[1].appendChild(h1);
するとタイトル画面に「Hello Youtube」という文字列が追加されました。area
という
コンテンツスクリプトは
コンテントスクリプトを適用するサイト、挿入したいCSS /JSファイル、挿入したいタイミングなどは全てmanifest.json
で定義します。
で定義した指定のサイトやパターンマッチングで適用サイトを選択し、``
コンテントスクリプトが実行するタイミングを変えたい
コンテンツスクリプトを書いたファイルにJavaScriptのwindow.onload
や jQueryのready()
といった、DOMの読み込みが完了したイベントを検知するコードを書いてないことに気づきましたか?普通、DOMを編集したいときは上記のようなイベントを検知したあと実行したい処理を書くことが多いと思います。なぜこれらなしでも動くのかというと、window.load
が発火した後にコンテンツスクリプトが実行されるからです。このようにコンテンツスクリプトが実行されるタイミングはmanifest.json
ファイルのcontent_scripts
内のrun_at
キーで変更することができます。
コンテントスクリプトが起動するタイミングは以下の3つがあります。
状態 | 説明 | 対応するJSのイベント |
---|---|---|
document_start |
DOMツリー構築中。DOMを組み立てている状態なので画面には何も表示されていない状態。document.body もdocument.head も空。 |
不明 |
document_end |
DOMツリーの構築完了。DOMへのアクセスができるようになる。しかし画像、CSS、JSなどの外部リソースはまだ読み込み始めてない。 |
DOMContentLoaded 発火後 |
document_idle (デフォルト) |
画像、CSS、JavaScriptなどの外部リソースの読み込み完了。生成される静的なDOMが完成されたタイミング。 |
load 発火後 |
JavaScriptのイベントについて理解を深めるためにDOMContentLoaded
とload
について細く説明をします。URLをクリックし、
特別なことがなければデフォルト値の document_idle
で十分なのでrun_at
は書かなくていいです。DOMを編集する目的でコンテンツスクリプトが使いたいなら、HTMLで書かれた要素だけでなくJSで動的に生成されるDOMにもアクセスできるように、ページが完成してからJS/CSSを挿入するdocument_idle
が確実でしょう。一方、処理に時間がかかるJSを挿入したいときはdocument_end
を使うといいでしょう。document_idle
を使うタイミングはわからない。
さらに遅く読み込む
コンテンツスクリプトを挿入するタイミングは3つあることを説明し、基本的にはdocument_idle
イベントを使えば十分だと説明しました。しかしもっと遅いタイミングでJS/CSSを読み込みたいタイミングがあります。特にReactやVueなど遅延ロードでページを構築するモダンなサイトにコンテンツスクリプトを使いたい場合だとdocument_idle
時でもまだアクセスしたい要素が生成されてないことがほとんどでsy。YoutubeやTwitterなど読み込む情報の多いサイトはまず画面の大枠のDOMを生成し、ページの読み込みが完了した(load
が発火されたタイミング)後でも、JSで動的に画面を構築します。これはYoutubeを使うとわかりやすいです。
このようにdocument_idle
よりも遅いタイミングでJS/CSSを挿入したい。そのためにはどうすればいいだろう。選択肢は2つある。
- setTimerで遅らせて処理を実行する
- MutationObserver でDOMの変化を監視する
しかし1の方法が無難だ。なぜなら入れ物のDOMすら生成されていない場合がある。MutationObserverで監視する要素すら存在しないからどうしようもない。
例えばスクレイピングの本でも要素が出てくるまで1秒待つ sleep(1000)
という処理をよくする。ずぼらに見えるが確実な方法だと思う。一方、一度限りの読み込みではなく、ページが生成された後でも不定期にDOMが変更される場合がある。例えばTwitterのように無限スクロールが使われてるサイトでは、ページの一番下までスクロールしたら新たにコンテンツが表示される。YoutubeやTwitchのようなライブ配信サイトでは、コメント欄に大量のコメントが流れてくる。このような場合はMutationObserver を使うのがいいだろう。入れ物のDOMがすでに生成されて、その中にのみコンテンツが追加されるからだ。他にも、不定期で更新されるコンテンツに対して、setIntervalのような定期的に検知する関数を使うとパフォーマンスが悪くなったり、コンテンツが現れるタイミングとそれを更新するタイミングがずれてユーザー体験が悪くなる。
こんな感じのが参考になるかも。setTimerが無難かな
runat
公式ドキュメント
Discussion