虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Chromeの拡張機能でショートカットを作る二つの方法

qiita.com

こんにちは、虎の穴ラボのH.Hです。
ついに今年も残り半月を切りました。本当にあっという間の1年だった気がします。
(私事ですが、本日でとらラボに入ってちょうど1年となります。入ったときは1年後完全リモートワークで働いているとは思ってもいませんでした。)

さてこの記事は虎の穴ラボ Advent Calendar 2020 - Qiitaの16日目の記事になります。

今回はChromeの拡張機能にキー入力のショートカット機能を追加実装してみたので、実装方法と動きについてまとめていきます。

ちなみに...
15日目はY.Iさんの「Pug+Stylusでお手軽にCSSお絵かきを始めよう!」です。

toranoana-lab.hatenablog.com

17日目はnsdさんの「Pepperくんを喋らせてみよう〜Pepperアプリ開発入門編〜」の予定です。

興味のある方はぜひご覧ください。

なぜ作ろうと思ったか

普段仕事でも趣味でもアプリケーションを作っているとき、公式ページや類似したことを行っているブログなどを探すことをよく行っています。
そのとき参考になるページもあれば、知りたいと思っていた内容と違うページも見つかります。
複数回検索を行っていると以前に見たページが再び出てくることがあるため、今後も見るページや一度見たけど知りたいと思ったこととは違うページをわかるようにして効率化したいと今考えています。
そのための前段階として、ページを見た記録をとる操作を簡単にするためにショートカット機能を実装してみようと思いました。

Chromeの拡張機能とは

Chromeの拡張機能とは何かですが、ChromeウェブストアなどからChromeに追加することで、標準には無い機能の追加を行うものです。
Google製のものはもちろんGoogle以外の企業が公開しているものも多くありますが、個人でも簡単に作成することができます。
以前にGoogleが公開しているチュートリアルの解説を行った記事も書いていますので、初めて作る人はこちらもご覧ください。

toranoana-lab.hatenablog.com

今回作る拡張機能

今回作る拡張機能に以下の機能を作ります。
・キーボードからの入力で表示されているページの背景色を変更する
・入力された内容によって変更する背景色を切り替える
・とらラボのページでだけ動作する
yumenosora.co.jp

準備するもの

今回は以下の環境で実施しました。
1.MacBook Pro(OSのバージョンは10.15.7)
2.Chrome (バージョン87)

キー入力を受け付ける仕組みについて

キー入力を受け付ける仕組みを調べると以下の2つの方法が見つかります。
1.content_scriptsで処理する
2.backgroundで処理する

仕組みや動作するタイミングが異なるので、作り方と動作の仕組みを解説します。

content_scriptsで処理する

作り方

1)準備したフォルダにmanifest.jsonという名前のファイルを作成します。

2)manifest.jsonの中身を以下のように記述します。

{
    "name": "任意の名前",
    "version": "1.0",
    "description": "任意の説明文",
    "permissions": [
    "tabs","activeTab"
  ],
    "icons": {
      "48": "images/get_started48.png"
    },
     "content_scripts": [{
      "matches": ["https://yumenosora.co.jp/tora-lab/*"],
      "js": [
        "content.js"
      ]
    }],
    "manifest_version": 2
  }

iconsは拡張機能を取り込んだ際のアイコンになります。
content_scriptsの中のmatchesでcontent_scriptsで定義したJavaScriptが動作するページを制限しています。
キー入力された際の処理を行うJavaScriptはcontent.jsとしています。名前は任意なので拡張機能の内容によってわかりやい名前に変えても問題ありません。

3)content.jsをmanifest.jsonと同じフォルダに作成する
機能が複雑になる場合は分けた方がわかりやすくなりますが、今回は単純な機能でファイル数も多くならないので同じフォルダに作成します。

4)content.jsに以下のコードを記述 上下左右のキー入力で画面全体の背景色を変更するようにしています。

(function (callback) {
  var script = document.createElement("script");
  script.textContent = "(" + callback.toString() + ")();";
  document.body.appendChild(script);
})(function () {
  document.addEventListener("keydown", function (e) {
    switch (e.which) {
    case 37:
      //left
      document.body.style.backgroundColor ="red";
      break;
    case 38:
      //up
      document.body.style.backgroundColor ="yellow";
      break;
    case 39:
      //right
      document.body.style.backgroundColor ="black";
      break;
    case 40:
      //right
       document.body.style.backgroundColor ="white";
      break;
    }
  });
});

フォルダ構成

f:id:toranoana-lab:20201207160016p:plain:w200

実行結果

作成したフォルダをブラウザに登録して実行します。
登録はchrome://extensions/にアクセスして、パッケージ化されていない拡張機能を読み込むからフォルダを選択すると取り込むことができます。
実行するとしたの画像のように上下左右のうち入力したキーに対応した色に背景色が変わります。 f:id:toranoana-lab:20201207163236p:plain

処理の解説

content_scriptsはブラウザに画面が表示されたタイミングでjsで定義されたファイルが実行されます。
今回は画面が表示された後のHTMLにキー入力によって発火するイベントを追加するJavaScriptが動いています。
ChromeのディベロッパーツールでHTMLの構造を確認すると元々のHTMLには存在しない、content.jsの内容が記述されていることがわかります。
f:id:toranoana-lab:20201207164853p:plain:w400

つまりcontent_scriptsで実装すると、厳密にはキーボードからの入力時にはChromeの拡張機能としては何も処理をしておらず、純粋なJavaScriptが呼び出されることによって背景色の書き換えが行われるという動きになります。

backgroundで処理する

作り方

1)準備したフォルダにmanifest.jsonという名前のファイルを作成します。

2)manifest.jsonの中身を以下のように記述します。

{
    "name": "任意の名前",
    "version": "1.0",
    "description": "任意の説明文",
    "permissions": [
    "tabs","activeTab"
  ],
    "icons": {
      "48": "images/get_started48.png"
    },
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "commands": {
    "test1": {
      "suggested_key": {
        "default": "Ctrl+Shift+1",
        "mac": "Command+Shift+1"
      },
      "description": "test1"
    },
    "test2": {
      "suggested_key": {
        "default": "Ctrl+Shift+1",
        "mac": "Command+Shift+2"
      },
      "description": "test2"
    }
    },
    "manifest_version": 2
  }

※実行時にcontent_scriptと動きの違いを見るために入力するキーを変更しています。
test1およびtest2は任意の文字列になります。重複がなければ自由に決めることができます。

commandsの内容の詳細についてはこちらをご確認ください。

developer.chrome.com

3)background.jsをmanifest.jsonと同じフォルダに作成する

4)background.jsに以下のコードを記述

chrome.commands.onCommand.addListener(function (command) {
  chrome.tabs.getSelected(null, function (tab) {
    tabUrl = tab.url;
    if (tabUrl.indexOf('https://yumenosora.co.jp/tora-lab') == 0) {
      if (command === 'test1') {
        chrome.tabs.executeScript(tab.id, {
          code: 'document.body.style.backgroundColor = "blue";'
        });
      } else if (command === 'test2') {
        chrome.tabs.executeScript(tab.id, {
          code: 'document.body.style.backgroundColor = "silver";'
        });
      }
    }
  });
});

フォルダ構成

f:id:toranoana-lab:20201207170929p:plain:w200

実行結果

content_scriptsと同様にブラウザに作成した拡張機能を登録します。
CommandとShiftに加えて1もしくは2を同時に押すと背景色が変わるようになります。

f:id:toranoana-lab:20201207171823p:plain

処理の解説

backgroundで処理する場合、manifest.jsonで定義したキー入力を待つ処理をbackground.jsに記述します。
content_scriptsの場合と違うのは、content_scriptsは画面表示時に1度だけ実行されますがbackgroundはChromeの中で常に待機しており、いずれかのタブで指定のキー入力が行われると処理されます。
そのため目的のサイトだけで処理を行う場合、現在有効なタブのURLなどから実行するかどうかを判別する必要があります。
また連携されてくる値はコマンドの名称(ここではtest1もしくはtest2)となるので、文字列による判定を記述する必要があります。
下の画像はbackground.jsにブレイクポイントを設置し、Command+Shift+1を入力した場合の画像です。
(background.jsは拡張機能のページのバックグラウンドページのリンクを押すと、ディベロッパーツールが開きブレイクポイントなどが設定できます) f:id:toranoana-lab:20201207173403p:plain

作り方による動作の違い

Chrome拡張機能でのショートカットの作り方による違いをまとめました。

- content_scriptsで作る場合 backgroundで作る場合
JavaScript実行タイミング 画面表示時 キー入力時
入力されたキーの定義 実行されるJavaScript内部 manifest.jsonに定義
特定のサイトで実行させる場合 △(実行できるがURLによる判別を作る必要がある)
複数のサイトで実行させる場合 △(JavaScriptを埋め込むので関数の重複などが起きる可能性あり)

まとめ

二つの方法で作成してみた所感ですが、コマンドをmanifest.jsonで定義しbackgroundで待機しているJavaScriptに処理を渡す方が、書き方としては正しいと感じました。理由はcontent_scriptsで処理を追加すると本来の処理に影響を与えてしまう可能性があるのと、ショートカットを作成するためにキーコードで分岐を作る必要が複雑になっていくためです。特に複数キーの同時入力を作る場合にミスが発生する可能性が大きいと感じました。
manifest.jsonで定義する場合、backgroundに渡されるコードが自分で決めることができ、組み合わせも明示的に記述するのでミスが発生しにくいと感じました。

また作成途中に躓いた点ですが、Chromeの拡張機能の画面で再読み込みで追加したコードを確かめていましたがmanifest.jsonに追加した内容が反映されないということがおきました。考えてみれば単純ですがmanifest.jsonを元にローカルのファイルを再度読み込む動きになっているので、manifest.json自体は読み込み対象になっていないために反映されていませんでした。(対応方法は拡張機能を一度削除して再度取り込み直す)

いくつかのChromeの拡張機能を作ってきましたが、まだまだ理解できていないところがあると今回作って感じました。

P.S.

虎の穴ラボではいくつかのオンラインイベントを企画しております。是非ご参加ください!!

【オンライン】とらのあなラボエンジニア座談会Vol.5【リーダー対談】

12/18(金) 19:30から「虎の穴ラボ」社員によるトークイベントを準備しております。 yumenosora.connpass.com

TORA LAB Management & Leader Meetup

12/23(金) 19:30からとらのあなが運営している「とらのあな通販」と「Fantia」の開発の魅力を発表し、参加いただいた方の気になる点やご質問に答えるイベントとなっています。 yumenosora.connpass.com

その他採用情報

虎の穴ラボでの開発に少しでも興味を持っていただけた方は、採用説明会やカジュアル面談という場でもっと深くお話しすることもできます。ぜひお気軽に申し込みいただければ幸いです。 カジュアル面談では虎の穴ラボのエンジニアが、開発プロセスの内容であったり、「今期何見ました?」といったオタクトークから業務の話まで何でもお応えします。 カジュアル面談や採用情報はこちらをご確認ください。 yumenosora.co.jp