HTML 要素を非表示にするブラウザ拡張

筆者は Web ページ上の広告 (特に動くタイプの広告, または美容・健康系) が非常に苦手だ。
特に美容・健康系 (人体の一部の画像/描写を含む) ものが生理的に受け付けず、とにかく目に入れたくない。
いったん目に入ると、読みたいページの内容が頭に入ってこないばかりでなく、しばらく記憶に残ってしまい、思考を阻害される気がする。
できるだけ広告ブロックを使用するようにはしているが、それでも防ぎきれないもの、あるいは広告ブロックだとページの表示に支障が出る/表示できないパターンがあり、 100% 広告から身を守ることが難しい。

そこでその場しのぎの対策としてよくやっていたのが、要素を (できるだけ注視しないようにしながら) 右クリックし、 "Inspect" で developer tool を起動し、コンソール上から $0.remove()
を実行して要素を一時的に消すというものである。
正直言って Web ページを開くたびにこんなことをやっていたら時間がかかってしょうがないし、作業が進まないのだが、それでもやらないとページの内容をまともに読めないので、やらざるを得ないのである。
とはいえこんなことをいちいちやっているのは面倒臭くてしょうがないので、ブラウザ拡張機能として実装できるんじゃないかと思った。

要求する機能自体は比較的単純なので、自分でも頑張ればつくれそうな気もしたのだが、ブラウザ拡張機能はつくったことがないし、基本的にフロントエンドの知識が無さすぎるので少々面倒である。
もしすでによくできたものがあれば使いたいと思った。
調べて出てきたのが、以下の 2 つ
おそらく同じような機能を提供しているのではないかと思うのだが、ユーザー数の多さなどから "Click to Remove Element" のほうを使ってみることにした

使い方は簡単
拡張機能のパネルから選択または ⌘+Shift+X
でアクティベートすると、左下に拡張機能 window が出てきて、選択要素がハイライトされる。
今回は Zenn のページで記事を書いた人のアイコンを選択してみる。
この状態で左クリックすると要素が見えなくなる。
いくつかのページで試したが、広告等も $0.remove()
を手でやるよりははるかに素早く隠せるのでなかなか良い。

自分でつくってみたい気持ちも多少あるし、何やってるかわからないとなんとなく不安なので一応ソースもちょっと見ておく。
Click to Remove Element の拡張機能のページからは blade.sk という著者の個人サイトらしきページへの link がある。
Portfolio のひとつとしてこの拡張機能が紹介されているが、ソース等は載せていない様子である。
拡張機能のソースを取得する方法を調べたところ、 CRX downloader という拡張機能を使うと拡張機能のファイル一式を zip 形式でダウンロードしてこれることがわかった。
拡張機能のストアのページから CRX downloader をアクティベートすると、 "Download as Zip" というボタンが出てきて、押すと拡張機能のファイル一式をまとめた zip がダウンロードできた。

Click to Remove Element の中身はこんな感じ
$ unzip -l Click-to-Remove-Element-Chrome-Web-Store.zip
Archive: Click-to-Remove-Element-Chrome-Web-Store.zip
Length Date Time Name
--------- ---------- ----- ----
9307 11-23-2023 10:27 background.js
6893 11-23-2023 10:28 content.css
29904 11-23-2023 10:27 ctre_content.js
0 03-14-2019 02:42 icons/
1496 11-28-2018 16:26 icons/action_active.png
1506 11-28-2018 16:26 icons/action_inactive.png
1454 11-28-2018 16:26 icons/action_unavailable.png
4535 10-10-2012 14:58 icons/icon_128.png
2201 10-10-2012 14:37 icons/icon_48.png
987 11-23-2023 10:26 manifest.json
0 11-23-2023 10:26 _metadata/
2082 11-23-2023 10:26 _metadata/verified_contents.json
--------- -------
60365 12 files
どうやら ctre_content.js
というのが実装本体らしい。
中身を見ると minify されていたりする様子はなく読めそう、良かった。
ライセンス自体は不明なため注意

簡単に実装を確認すると、以下のような流れで処理しているのがわかった。
- 拡張機能のアクティベート時に各種 eventhandler を追加
ctre.targetingMode = true
document.addEventListener('mouseover', ctre.handleMouseover, true)
document.addEventListener('mousedown', ctre.hideTarget, true)
document.addEventListener('mouseup', ctre.preventEvent, true)
document.addEventListener('click', ctre.preventEvent, true)
document.addEventListener('scroll', ctre.updateHighlighterPos, true)
-
mouseover
された要素をハイライトする
handleMouseover: function(e) {
...
if (ctre.hoveredElement != e.target) {
ctre.transpose = 0;
ctre.hoveredElement = e.target
ctre.highlightElement()
}
},
-
mousedown
でハイライト中の要素をhiddenElements
という配列に追加, CSS を更新
hideTarget: function(mouseEvt/* optional */) {
...
let selector = ctre.getSelector(ctre.markedElement)
if (!selector) return
...
ctre.unhighlightElement()
ctre.hiddenElements.push({
selector,
permanent: !!ctre.settings.remember,
})
...
ctre.updateCSS()
...
},
-
updateCSS()
でhiddenElements
にdisplay: none
属性を指定する
for (let i in ctre.hiddenElements) {
let selector = ctre.hiddenElements[i].selector
if (selector == ctre.previewedHiddenSelector) {
cssLines.push(selector + ' { outline: solid 5px rgba(0,214,255,0.5) !important; outline-offset: -5px; }')
} else if (selector == 'body' || selector == 'html') {
cssLines.push(selector + ' { background: transparent !important; }')
} else {
cssLines.push(selector + ' { display: none !important; }')
}
}
...
styleElm.appendChild(document.createTextNode(cssLines.join('\n')))