久々のQiita投稿🙌
普段はその辺でエンジニアやってる物です。
個人で使うめちゃくちゃニッチな物を作ったので、せっかくなので記事にしてみました。
そしてここから下、**「cmd + クリックでええやん」**禁止です🙅♂️
それすらめんどくさかったので作った所存です。
何を作ったの?
GitHub上でリンクをクリックした時に遷移先が同じ画面で開かれるのがしんどかったのでChromeの拡張機能を作った話です。
作ったものはこちら。
Chromeウェブストアに公開しているので、よかったらダウンロードして使ってみてください。
ちなみに、2020年9月現在のGitHub上のクラスやIDを利用しているので、GitHub上のHTML構造が変更された場合突然動かなくなることも考えられます。
メンテを続けるかどうかはまだ決めてません。
GitHubのしんどさとは
コードレビューをGitHub上で行う時に、PR画面のConversation内にコミットハッシュを書くと自動でリンクになってくれる便利機能があります。
こんな感じですね。
マウスオーバーでコミットログとかみられるんですけど、見たいのはそこじゃない、
そのcommitでどのファイルをどうやって修正しているかなんだ。
あとこんな時。
こんなものに他の方に協力仰ぐのも申し訳なかったので以下全部自演コメントです🙇♂️
こんなやりとり数多くあったのでGitHubユーザの方ならきっと想像できるはず。
レビュアーとしては、
「お、修正してくれたんだ〜〜修正内容どんな感じかな〜〜〜〜」って
コミットログのリンクを押して確認しようとすると、どういうわけか同じタブで画面遷移します
もちろん、戻るボタンで戻っても入力したコメントとかは消えないですよ?
でも普通に考えると、別タブで開いて修正履歴じっくり見たい時のが多く無いですか?
あと、他にもPRの概要を読みながらおもむろにFiles changed
タブとか押してPR全体のファイル変更履歴とか見たくないですか?
そんな要望に応えることができるChrome拡張を作りました。
その名もgithub-target-blank
です。
工夫したところ
アプローチとしてまず考えたのが、
別タブで開きたいa要素に対して target='blank'
を動的に付与する
というやり方。
(rel='no-oppener no-referrer'も一緒に)
GitHubのページの特定のa要素のみtarget='blank'
を付与するだけなので、そこまで大変では無いやり方ですね。
ただこのやり方だと、開かれたページがアクティブな状態になります。
そう、PRのConversationを見ているのにリンクを押すと別のタブがアクティブになってしまうわけですね。
違う、違うんだ。
僕が欲しいのはGoogle Chromeで cmd + クリックでリンクを押した時のバックグラウンドでタブが開かれるあの動きなんだ。
というわけでこのアプローチは断念。
別のアプローチをなんか無いかなーと探していたところ、こんなメソッドを発見。
MouseEvent インターフェイスは、ポインティングデバイス (マウスなど) によるユーザの対話によって発生したイベントを表します。
このインターフェイスを使用する一般的なイベントとして click, dblclick, mouseup, mousedown があります。
ふむふむ。あれこれなんか使えるくね?
っていうか、擬似的にa要素作って、MouseEventで擬似a要素をDispatchしちゃえばいいんじゃね?
って思って探したらありました。さすがはStackOverFlow。
https://stackoverflow.com/questions/10812628/open-a-new-tab-in-the-background
注意
ここで紹介されてるinitMouseEvent
はDeprecatedのメソッドのため、利用非推奨です。
代替機能として、MouseEvent
が推奨されてます。
おーできそう、ってことでコード書いてみました。
メインの部分はこんな感じ。
ChromeのExtensionにはjQueryも使えるっぽいけど、サイト内でバージョン競合とか起こすと嫌なので久々にVanillaJS。
hoge.addEventListener("click", function (e) {
const evt = new MouseEvent("click", {
metaKey: true, // Macだとcmdキーが metaKeyとして判定される学び
ctrlKey: true, // Windowsだとバックグラウンドで開く時に使うのがctrlキーなのでこちらも設定
});
const a = document.createElement("a");
a.href = e.currentTarget.url; // 遷移先のURL
a.dispatchEvent(evt); // ここでMouseEventイベントを擬似的に作ったa要素に向けて発火
e.preventDefault(); // 本来のa要素での移動をキャンセル
});
急に思い立ってChromeのextensionを作り始めた。MacのCmdキーはMetaKey扱いだということを知った。https://t.co/V5ybBD2mND
— yoshi.@code-plum (@codeplumdev) September 6, 2020
Macの場合cmdキーがmetaKeyになるとか知らなんだ。。。20分ぐらいハマりました。
ってなわけで、GoogleChromeでcmd(ctrl) + クリックした時にバックグラウンドでタブが開くあの動きが再現できました、わーい。
苦労したところ
ChromeExtensionの仕組みわからなすぎ問題
開発にあたり一時間ぐらいこのページとにらめっこしてました。
三つの世界の世界観がマジでわからなかったので、@sakaimoさんの記事を参考にしました。圧倒的感謝😭🙇♂️💦
https://qiita.com/sakaimo/items/416f36db1aa982d8d00c
特定のページだけで利用したい時はContent Scripts
、
アイコン(Extensionインストール後に出てくる右上のあれ)クリック時に何かしたい時はBrowser Action (Page Action)
、
Chrome起動中ずっと動かしときたい時はEvent Page
って区分のようです。
今回はGitHub上で動けばよかったのでContent Script
を採用してましたが、そのうち「これはバックグラウンドでこれは普通に開きたい」とかあるだろうなぁという気持ちがあったため、extensionの設定をローカルストレージに保存するようにするために、
popup.js上で設定変更
↓
background.jsにイベント(任意のメッセージ)を送信
↓
background.js内で設定内容をローカルストレージに保存
↓
contents.js発火時にローカルストレージ上の設定をbackground.js経由で取得
↓
有効な設定要素のみ、addEventListenerを実行
っていうフローを取ってます。結局全部使ってる。
pjax対応
GitHubのHTMLElementsを眺めてるとどうやらpjaxが利用されているようで、ページ遷移した時にうまく動かないバグがありました。
この辺りはMutationObserver
を使ってDOMを監視し、変更されたタイミングで再度content.jsを発火するようにしています。
const target = document.getElementById(observerSettings.selector);
// 現状だと github.com がホストに含まれるときは実行するようになっているため、
// 特定ページ意外で発火したときのエラーを防ぐために、getElementByIdで取得した要素が存在する場合のみ
// MutationObserverを利用するようにしている
if (target) {
const observer = new MutationObserver((mutations) => {
// ここで任意の処理を発火することができるので、メイン処理を再度実効
});
observer.observe(target, observerSettings.config);
}
さいごに
久々にVanillaのJSでコーディングしたり、Chromeの拡張機能の仕組みに混乱したり、
「よっしゃできたーーー」ってなってたらinitMouseEventが利用非推奨なことに気がついて全部作り替えることになったりしましたが、
ChromeのExtensionは手軽にコーディングできる良い環境だと思いました。
なんだか、GoogleChromeで急に回転するポプ子とピピ美が見たくなった気がしたので、クソアプリアドベントカレンダーにでもエントリーしようかなと思った今日この頃。
なんやかんや言ってますが楽しくコーディングできました。
皆さんも、是非ChromeのExtensionを作ってみては如何でしょうか。
コードレビュー時のあーーーーーいまのページ移動しないで欲しいのーーーも解消されたので、本日はここまでにします。
同じような悩みがある方、是非使ってみてください。
ご意見ご感想PRお待ちしております。
おまけ
引数15個の公式メソッドは初めて見た。
— yoshi.@code-plum (@codeplumdev) September 6, 2020
流石にヤバイなと思って調べてたら非推奨の機能でクラス化されてて安心したhttps://t.co/SreRgLVtyehttps://t.co/0v9UtB5xw5
・・・きっと大変なことがあったんだろうと思いましたまる