最近Chrome拡張機能について考える機会が多く、自分も何か作ってみることにした。かねてから「自分もChrome拡張の一つや二つ作れなくてなは~」などと考えていたものの、特にアイデアがあるわけではないので、放置していた。 しかし丁度良くアイデアが舞い降りてきたので、やってみることにした。
heading要素にパーマリンクが欲しい
先日Scala 3.0.2のリリースノートを読んでいたとき、良いなと思った記述があった。具体的には次の箇所。
Scala 3.0.2 released! | The Scala Programming Language
型情報をもとに、メソッドを検索できるようになるらしい。さしずめ、HoogleのScala版といったところ。面白いのでここのリンクを社のSlackに投稿しようとした。
この箇所はh3
要素を筆頭に記述されている。h3
要素にid
属性があるのでフラグメント記法を使ってhttps://www.scala-lang.org/blog/2021/09/07/scala-3.0.2-released.html#method-search-by-type-signature
というURIを構成できるし、この記法でリンクを貼ればブラウザはすぐにこの箇所を表示してくれるだろう。
最近のサイトでは、heading要素にid
属性があるようなパターンでは、見出しの右側にリンク🔗の絵文字が表示され、クリックするとリンクをコピーできるのだが、今回のサイトにはそのような仕組みがなかったので、開発者コンソールを開いてid
属性の中身をコピーしなければならなかった。
この機能はChrome拡張機能で実現できそうだし、それほど実装に手間がかかることはなさそうだと思ったので、さっそく作ることにした。
できた
最低限の機能を実装して作成したのがこちら。
h2
, h3
, h4
要素にid
属性があるときは、自動的にパーマリンクを作成してくれるようになった(図では見出しの末尾に#
記号が出現している)。
やったことは以下の通り。
- 実装となる
content_script.js
の実装 - アイコン各種の作成(PNG形式)
manifest.json
の作成
最低限これだけでなんとかなる。実装には以下のサイトを参考にした。
この記事のタイトル通り、この拡張機能は会社の昼休みに作れてしまった。
content_script.js
ページロード時にdocument
内のh2[id],h3[id],h4[id]
をかき集めてきて、リンクとなるa
要素を末尾にくっつけている。
window.onload = function() { const elems = [...document.querySelectorAll('h2[id], h3[id], h4[id]')]; elems.forEach((e) => { const link = document.createElement('a'); link.innerText = '#'; link.href = location.href + '#' + e.id; const small = document.createElement('small'); small.append(link) const sub = document.createElement('sub'); sub.append(small); e.append(sub); }); }
これだけ。
アイコン各種
GIMPで適当に作成した。id
属性があるところにリンクを追加するので、id
を象徴する#
記号と、リンクを象徴するアンダーラインを使ったものにした。
ちなみにここで反省点がある。知識不足から先に32x32ピクセルのアイコンを作成してしまったので、128x128ピクセルの画像がぼんやりになってしまった。実際は32x32があれば良いっぽい?のだけれど、一番先に128x128や、より大きいサイズでアイコンを作成して、適宜縮小したほうがよいと思う。
manifest.json
の作成
拡張機能にはmanifest.json
が必須。最小限name
とmanifest_version
、content_scripts
あたりを埋めればよさそうだった。
{ "name": "ID Heading Autolinker", "description": "Insert permalink automatically along h2, h3, h4 elements with ID attribute.", "manifest_version": 2, "version": "0.1", "author": "windymelt", "homepage_url": "https://github.com/windymelt/id-heading-autolinker-extension", "browser_action": { "default_icon": "icon_32.png" }, "icons": { "16": "icon_16.png", "48": "icon_48.png", "128": "icon_128.png" }, "content_scripts": [ { "matches": ["http://*/*", "https://*/*"], "js": ["content_scripts.js"] } ], "permissions": ["activeTab"] }
バージョン番号はいかにも管理漏れしそうで怖い。
この通り記述すると、ブラウザがタブを開くたびにcontent_scripts.js
が実行される。
動作確認
ローカルで動作確認するためには、拡張機能の管理画面から「パッケージ化されていない拡張機能を読み込む」を選択し、manifest.json
があるディレクトリを指定すればよい。
これで拡張機能が有効化された。
冒頭に登場したScala3.0.2のリリースノートを見ると、ちゃんとリンクが出現していることがわかる。
いまのところちゃんと動作しているようで、困ったことは起こっていない。積年の面倒臭さが解消されたのでとてもうれしい。
Chrome Web Storeに登録する
せっかく作ってちゃんと動作したからには他の人にも使ってもらいたいと思ったので、Chrome Web Storeへの登録にもチャレンジしてみた。Chrome Web Storeへの登録に関しては公式のドキュメントを参照した。
不正利用を防ぐためか、初回登録時に5ドルGoogleに御布施しなければならない。ドイツでトイレを借りたときも、このくらい(ユーロで)取られた記憶がある。
基本的にフォームを埋めていくだけなのであまり難しいことはない。気を付けたのは以下の点。
- パーミッションの範囲が広い(今回は全サイトで有効化する拡張機能な)ので、その理由を1行書く必要がある。
- ZIPファイルをアップロードする必要がある。今回はGithubのリリース機能を使ってZIPを作り、それをそのままアップロードした。
- スクショを添付する必要がある。GIMPでちまちま指定のサイズのスクショを作成した。
- 拡張機能の説明文を英語で書くことになるけれど、拙い英語力がバレるかもしれないので不安になった。
パーミッションの範囲が広いので今回はしばらく審査待ちになるようで、デベロッパーコンソールの画面はこういう状態になっている。
感想
意外と簡単だった。もっと機能が複雑になってきたらnpmを使ったりTypeScriptを使いたくなりそう。すると今回使った「雑にGithub Releaseを作成してZIPを得る」方式が通用しなくなるので、別のビルドラインを作ることになるのかなぁ、と思った。この拡張機能で大儲けしたい。
闇でインストールしたい方向け情報
GithubのReleaseからZIPファイルをダウンロードできるので、解凍すると「パッケージ化されていない拡張機能を読み込む」で利用することができます。At Your Own Risk.