紹介
ここで配布しています。インストール方法なども書いてあります。
AtCoder のユーザ名やレーティングの茶色・緑色、ついでに水色も変更されます。AtCoder Problems も同様の変更が起こります。
こんな感じです
↓↓ これが
↓↓ こうなる
見やすい、かな?
以下からはだらだらした説明と、実装方法が書かれているので読んでくれる人は読んでね!
はじめに
改めましてどうも、わたしです。AtCoder をせこせこ頑張っているわたしですが、つい先日緑Coder になることができましたありがとうございます。ところで、わたしは色覚異常なので、色が見づらいことがあるんですよね。たとえばこれ
AtCoder のレーティングなんですけど、上が茶色で下が緑色です。わかります?まあ、普通はわかるのかもしれない。でも、ぱっと見せられたらわたしには区別ができないし、どっちが何色なのかもわからないんです。ただ、レートは 400 刻みなので数字の認識から違う色だとわからなくもないが、それは置いといて
もいっこおまけに AtCoder Problems も
これだけ近づけば、茶色と緑かなってわかりますけど、あの表でぱっと見せられるとすぐには認識できないんですよ。なので仕方なく、このリンクを「検証」して、要素の style 属性の中身をみて、「あ、brown なんだ、green なんだ」とかやって diff を判断してたんです。
ってことで、これをなんとか解決したいと思ってたんです。Chrome の拡張機能でいけそうだなとは思っていて、ストアも調べたんですがなくて、Twitter とか調べれば同じ症状の人いそうですが、まあ自分で作ってしまおうと思いました。
スタイルを変える
さて、今回は「Web ページの書き換え」を行って色の変更を試みるという感じになりますね。
まずは AtCoder の方を調べてみましょう。
自分の成績表に書いてあるここから、この色がどう決まっているのかを開発者ツールでのぞき見します。
どうやら、user-green
というクラスで決められているようです。
この user-green
は base.css
によって、スタイルの設定がされていて、color
が #008000
になっていますね。
base.css
はこのようになっていて、色の指定が丸裸です。各色ごとにクラスがあって、color がスタイルとして指定されているということは、これらのスタイルを書き換えてしまえばいいです。CSS のスタイルなら上書きができるので行けそうですね!
次のファイルを作成してみましょう。
.user-brown {
color: #8a3b2c !important; /* #804000 から変更 */
}
.user-green {
color: #78c496 !important; /* #008000 から変更 */
}
.user-cyan {
color: #68c8f2 !important; /* #00C0C0 から変更 */
}
!important
はスタイルの上書きを強制的にできる合言葉です。「重要!!」ってことなので無視しないでね、みたいな感じ?
色はそれぞれユニバーサルデザインとされているものを採用しました。今回は緑と茶色ですが、水色もなんかあれだったので一緒に変えます。
茶 : #804000
-> #8a3b2c
緑 : #008000
-> #78c496
水 : #00C0C0
-> #68c8f2
こんな感じ。緑色はだいぶ明るくなっています。コントラストが変わるとみやすさがだいぶ違うんですよね。
つぎはこれを適用させてみましょう。ここから、拡張機能を作っていきます。
拡張機能をつくる
Chrome の拡張機能を作るには manifest.json
という JSON のファイルが必須です。さっきのスタイル style.css
と一緒に同じディレクトリにおきましょう。
atcoder-universal-color/
└ src/
├ style.css
└ manifest.json
この manifest.json
に必要な情報を書いていきます。
{
"name": "AtCoder Universal Color",
"description": "もう緑と茶色で迷わない!",
"version": "1.0",
"manifest_version": 3,
"content_scripts": [
{
"matches": ["https://atcoder.jp/*"],
"css": ["style.css"]
}
]
}
name
はこの拡張機能の名前です。今回は AtCoder Universal Color としました。
description
は説明を書きます。拡張機能の管理画面で出てくるちょっとした説明書きです。
version
はこの拡張機能本体のバージョンです。公開するなら設定とかしとくといい?
manifest_version
これはマニフェストの形式のバージョンで現時点では 3 が最新なので 3 を書いておきます。
content_scripts
ここが本体です。ここに、どのサイトで実行して、何を適用するかを指定します。今回は AtCoder のサイトで、さっきのスタイルを適用したいので matches
に AtCoder の URL を、スタイルなので css
に style.css
を指定してあげます。
これで拡張機能は完成です。なんとあっさり!これを早速導入してみよう!
自作拡張機能の導入
まず、Chrome の拡張機能管理に行きます。アドレスバーに chrome://extensions
としてもいいし、拡張機能のアイコンを押して、管理ページ飛んでもいいです。
こんな画面が出てくると思います(真っ白でモザイクをかけてる)。ここの右上にある「デベロッパーモード」を ON にしましょう。
すると、なんかタブが出てきます。この「パッケージ化されていない拡張機能を読み込む」を押すと、フォルダを指定する画面が出てきます。ここではさっき作った manifest.json
や style.css
があるディレクトリ(今回だと src
ディレクトリ)を指定してあげます。これで導入は OK です。
しっかり拡張機能として認識できているようです。ここで何か失敗があるなら、manifest.json
などが間違えてしまっていると思うので修正しましょう!
さて、もういけます。AtCoder のサイトにアクセスすると ...
ちゃんと変わりました!すごい!見やすくなった!!
でも、上向きの矢印は変わってないですね。これ、画像ファイルっぽいので、スタイルの変更じゃできないところでした。まあ、パフォはすごく見やすくなったので OK かな?
(一部表の順序を変更しています)
AtCoder Problems の番
もう行けるでしょう、ってことで Problems の方もやっちゃいましょう。
さっきと同じで、どうやって色が決まっているのかを開発者ツールからひっそりのぞきます。
お、どうやらこれもクラスでスタイルが決められているようですね!さっきと一緒でスタイルシートを上書きしちゃえばよさそうです。style.css
に追記しましょう!
.user-brown {
color: #8a3b2c !important; /* #804000 から変更 */
}
.user-green {
color: #78c496 !important; /* #008000 から変更 */
}
.user-cyan {
color: #68c8f2 !important; /* #00C0C0 から変更 */
}
.difficulty-brown {
color: #8a3b2c !important;
}
.difficulty-green {
color: #78c496 !important;
}
.difficulty-cyan {
color: #68c8f2 !important;
}
これで上書き保存して OK です!マニフェストも忘れずに書き換えましょう。
{
"name": "AtCoder Universal Color",
"description": "もう緑と茶色で迷わない!",
"version": "1.0",
"manifest_version": 3,
"content_scripts": [
{
"matches": ["https://atcoder.jp/*"],
"css": ["style.css"]
},
{
"matches": ["https://kenkoooo.com/atcoder"],
"css": ["style.css"]
}
]
}
適用する Web ページを増やすには content_scripts
に追記していきます。AtCoder Problems の URL を入れて OK です。この変更を Chrome に反映させます。
拡張機能の管理画面でここを押せば反映できます。さあ AtCoder Problems のサイトにアクセスしてみると ...
おーーー変わってい、ますけど、あれ?問題名の色だけが変わって、難易度の丸が変わってないですね ...
そうなんです、実は difficulty-color
というクラス、問題名にしか使われていないんです。この丸はどうやら別で色が決まっているみたい?さっきの HTML をもっかい見てみましょう
どうやら丸を作っているのは span
要素で、これのスタイルによって直書きされていますね。でもクラスとして difficulty-circle
があるので、ここのスタイルとしてできそう?いや、さっきは difficulty-brown
ってクラスが色ごとに分かれていたからできたけど、これは一緒になっちゃってるし、色ごとにクラスが分かれていそうにもないのでスタイルの上書きはできなさそうです。困った!
ここの色指定、JS ファイルとかをたどっていくと正体とかが分かるんですが、既存の JS ファイルの書き換えはできない(はずな)のでもう最終手段しかありません。
そうです、置換です。
JS で置換
もう HTML 文書そのものを書き換えてしまいましょう。それをするには JavaScript を使います。あら便利、そんなこともできるのね
具体的な説明は省いて、次のようなスクリプトを用意します。
var circles = document.getElementsByClassName("table-problem");
let old_green = "rgb(0, 128, 0)";
let old_brown = "rgb(128, 64, 0)";
let old_cyan = "rgb(0, 192, 192)";
let new_green = "rgb(120, 196, 150)";
let new_brown = "rgb(138, 59, 44)";
let new_cyan = "rgb(104, 200, 242)";
for (let i = 0; i < circles.length; i++) {
var circ = circles[i].innerHTML;
circ = circ.replaceAll(old_green, new_green);
circ = circ.replaceAll(old_brown, new_brown);
circ = circ.replaceAll(old_cyan, new_cyan);
document.getElementsByClassName("table-problem")[i].innerHTML = circ;
}
まず、document
はこの Web ページ全体です。これに対してメソッド getElementsByClassName(class_name)
を起こすと、class_name
に指定したクラスを持つ要素の中身がリストみたいな感じで入手できます。今回は table-problem
というクラス名を指定していますが、これはさっきの難易度の丸の中身を見たときに td
要素につけられていたクラスです。
これで、<span から始まるこの td
要素の中身をすべて取得できます。そして、circles
にはクラス table-problem
をもつ全 td
要素の中身が入っています。ということはこの circles
の各メンバーに対して、書き換えを行っていけばいいですね!
script.js
の続く let
は色指定です。HTML 文書上だと RGB 表記になっていたので、RGB 表記で書いています。置換をするので元の方は合わせなければなりません。
次の for 文で置換の処理を行っています。
for (let i = 0; i < circles.length; i++) {
var circ = circles[i].innerHTML;
circ = circ.replaceAll(old_green, new_green);
circ = circ.replaceAll(old_brown, new_brown);
circ = circ.replaceAll(old_cyan, new_cyan);
document.getElementsByClassName("table-problem")[i].innerHTML = circ;
}
まず、circles
のメンバーの数だけ for を回します。で、そのメンバーを取り出すんですけど、innerHTML
を後ろに付けます。これをすることで、 とかのタグなどを残したまま HTML 文書の文字列として取得することができます。
JS の文字列は replaceAll
で置換ができます。置換前、置換後の順で書きます。3 色分置換するので 3 行書きましょう。ちなみに replaceAll
は書き換えたものを新しく作るので、置換は元の変数に再代入する必要があります。
最後に、変更を反映します。さっきと同様にクラス table-problem
を取り出し、変更後の文字列を代入してあげます。これで OK です。もう Web ページの書き換えができちゃいます!!
じゃ、早速拡張機能に入れる、前に、本当にできるか試してみましょう。開発者ツールのコンソールにさっきのスクリプトを貼り付けて実行してみます。
するとこの通り!!!
ちゃんと難易度の丸も色が変わっていますね!!やったー
よーしこれで晴れて拡張機能にも入れられます。JS の指定はマニフェストに次のように書けば OK です!
{
"name": "AtCoder Universal Color",
"description": "もう緑と茶色で迷わない!",
"version": "1.0",
"manifest_version": 3,
"content_scripts": [
{
"matches": ["https://atcoder.jp/*"],
"css": ["style.css"]
},
{
"matches": ["https://kenkoooo.com/atcoder"],
"css": ["style.css"],
"js": ["script.js"]
}
]
}
最後の行に js
という部分が追加されています。これで OK です。よーし拡張機能の修正を反映していくぞー、と?
あれれー丸が変わってなーーい、なんで?コンソールからやったら OK だったのに、うーんなんでー
って感じで、失敗してしまいます。
スクリプトの実行を遅らせる
どうして失敗してしまうんだ!!これはたぶん、この難易度の丸が生成される前に拡張機能のスクリプト実行されてしまうのが原因だと思います。難易度の丸、どう考えても内部スクリプトで作られてるはずなので、実行のタイミングが大事なんですね。
結論からいうと、スクリプトを次のように変更します。
window.addEventListener("load", main, false);
function main(e) {
const jsInitCheckTimer = setInterval(jsLoaded, 500);
function jsLoaded() {
if (document.getElementsByClassName("table-problem").length > 0) {
clearInterval(jsInitCheckTimer);
var circles = document.getElementsByClassName("table-problem");
let old_green = "rgb(0, 128, 0)";
let old_brown = "rgb(128, 64, 0)";
let old_cyan = "rgb(0, 192, 192)";
let new_green = "rgb(120, 196, 150)";
let new_brown = "rgb(138, 59, 44)";
let new_cyan = "rgb(104, 200, 242)";
for (let i = 0; i < circles.length; i++) {
var circ = circles[i].innerHTML;
circ = circ.replaceAll(old_green, new_green);
circ = circ.replaceAll(old_brown, new_brown);
circ = circ.replaceAll(old_cyan, new_cyan);
document.getElementsByClassName("table-problem")[i].innerHTML = circ;
}
}
}
}
まず、window
の addEventListener
で load
が発火するのを待ちます。load
は HTML や CSS、画像の読み込みが完了すると発火するので、ページの読み込みが終わると、main
という関数が呼ばれる感じになっています。
つぎでは setInterval
で jsLoaded
という関数を 500 ミリ秒ずつ繰り返し処理します。これに jsInitCheckTimer
という名前をつけておき、あとでキルするのに使います。
jsLoaded
では何をするかというと、さっきまでの問題はおそらくクラス table-problem
が完成する前にスクリプトを実行しちゃったので色が変わらなかった、という問題だったと思います。なので、table-problem
がしっかりモノを持つまで待ち続ける必要があります。getElementsByClassName
は配列が返ってくるので長さが 1 以上になるまで待ちます。
table-problem
が完成したら if の中に入ってくれます。もう待つ必要がないので、jsInitCheckTimer
は殺しておきます。あとはさっきまでの色置換の処理を行えば OK です!!
改めて変更を反映して、AtCoder Problems のサイトにつないでみると
1 秒くらい経ったあとにしっかり難易度の丸も色が変わりました!!成し遂げたぜ
おわりに
という感じで、拡張機能を実装しました。CSS の上書きと、要素の置換という 2 つを行っていますが、結構勉強になることも多かったです。
ゆくゆくは上向き矢印もちゃんと変わるようにアプデしたいね。
参考
2. 実践編 - Chrome 拡張機能を作ろう
JavaScript:class要素のHTMLをreplace。
動的なページの読み込みが完了してからChrome拡張機能を実行する方法 #JavaScript - Qiita