2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AtCoder の茶色と緑色が見にくいので Chrome の拡張機能を作った

Last updated at Posted at 2025-03-11

紹介

ここで配布しています。インストール方法なども書いてあります。

AtCoder のユーザ名やレーティングの茶色・緑色、ついでに水色も変更されます。AtCoder Problems も同様の変更が起こります。

こんな感じです

↓↓ これが

image.png

↓↓ こうなる

image.png

見やすい、かな?

以下からはだらだらした説明と、実装方法が書かれているので読んでくれる人は読んでね!

はじめに

改めましてどうも、わたしです。AtCoder をせこせこ頑張っているわたしですが、つい先日緑Coder になることができましたありがとうございます。ところで、わたしは色覚異常なので、色が見づらいことがあるんですよね。たとえばこれ

image.png

AtCoder のレーティングなんですけど、上が茶色で下が緑色です。わかります?まあ、普通はわかるのかもしれない。でも、ぱっと見せられたらわたしには区別ができないし、どっちが何色なのかもわからないんです。ただ、レートは 400 刻みなので数字の認識から違う色だとわからなくもないが、それは置いといて

もいっこおまけに AtCoder Problems も

image.png

これだけ近づけば、茶色と緑かなってわかりますけど、あの表でぱっと見せられるとすぐには認識できないんですよ。なので仕方なく、このリンクを「検証」して、要素の style 属性の中身をみて、「あ、brown なんだ、green なんだ」とかやって diff を判断してたんです。

image.png
↑ HTML の中身を見ると色の答えが書いてある

ってことで、これをなんとか解決したいと思ってたんです。Chrome の拡張機能でいけそうだなとは思っていて、ストアも調べたんですがなくて、Twitter とか調べれば同じ症状の人いそうですが、まあ自分で作ってしまおうと思いました。

スタイルを変える

さて、今回は「Web ページの書き換え」を行って色の変更を試みるという感じになりますね。

まずは AtCoder の方を調べてみましょう。

image.png

自分の成績表に書いてあるここから、この色がどう決まっているのかを開発者ツールでのぞき見します。

image.png

どうやら、user-green というクラスで決められているようです。

image.png

この user-greenbase.css によって、スタイルの設定がされていて、color が #008000 になっていますね。

image.png

base.css はこのようになっていて、色の指定が丸裸です。各色ごとにクラスがあって、color がスタイルとして指定されているということは、これらのスタイルを書き換えてしまえばいいです。CSS のスタイルなら上書きができるので行けそうですね!

次のファイルを作成してみましょう。

style.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 に必要な情報を書いていきます。

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 を、スタイルなので cssstyle.css を指定してあげます。

これで拡張機能は完成です。なんとあっさり!これを早速導入してみよう!

自作拡張機能の導入

まず、Chrome の拡張機能管理に行きます。アドレスバーに chrome://extensions としてもいいし、拡張機能のアイコンを押して、管理ページ飛んでもいいです。

image.png

こんな画面が出てくると思います(真っ白でモザイクをかけてる)。ここの右上にある「デベロッパーモード」を ON にしましょう。

image.png

すると、なんかタブが出てきます。この「パッケージ化されていない拡張機能を読み込む」を押すと、フォルダを指定する画面が出てきます。ここではさっき作った manifest.jsonstyle.css があるディレクトリ(今回だと src ディレクトリ)を指定してあげます。これで導入は OK です。

image.png

しっかり拡張機能として認識できているようです。ここで何か失敗があるなら、manifest.json などが間違えてしまっていると思うので修正しましょう!

さて、もういけます。AtCoder のサイトにアクセスすると ...

image.png

ちゃんと変わりました!すごい!見やすくなった!!
でも、上向きの矢印は変わってないですね。これ、画像ファイルっぽいので、スタイルの変更じゃできないところでした。まあ、パフォはすごく見やすくなったので OK かな?
(一部表の順序を変更しています)

AtCoder Problems の番

もう行けるでしょう、ってことで Problems の方もやっちゃいましょう。
さっきと同じで、どうやって色が決まっているのかを開発者ツールからひっそりのぞきます。

image.png
image.png
image.png

お、どうやらこれもクラスでスタイルが決められているようですね!さっきと一緒でスタイルシートを上書きしちゃえばよさそうです。style.css に追記しましょう!

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 です!マニフェストも忘れずに書き換えましょう。

manifest.json
{
	"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 に反映させます。

image.png

拡張機能の管理画面でここを押せば反映できます。さあ AtCoder Problems のサイトにアクセスしてみると ...

image.png

おーーー変わってい、ますけど、あれ?問題名の色だけが変わって、難易度の丸が変わってないですね ...

そうなんです、実は difficulty-color というクラス、問題名にしか使われていないんです。この丸はどうやら別で色が決まっているみたい?さっきの HTML をもっかい見てみましょう

image.png

どうやら丸を作っているのは span 要素で、これのスタイルによって直書きされていますね。でもクラスとして difficulty-circle があるので、ここのスタイルとしてできそう?いや、さっきは difficulty-brown ってクラスが色ごとに分かれていたからできたけど、これは一緒になっちゃってるし、色ごとにクラスが分かれていそうにもないのでスタイルの上書きはできなさそうです。困った!

ここの色指定、JS ファイルとかをたどっていくと正体とかが分かるんですが、既存の JS ファイルの書き換えはできない(はずな)のでもう最終手段しかありません。

そうです、置換です。

JS で置換

もう HTML 文書そのものを書き換えてしまいましょう。それをするには JavaScript を使います。あら便利、そんなこともできるのね

具体的な説明は省いて、次のようなスクリプトを用意します。

script.js
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 要素につけられていたクラスです。

image.png

これで、<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 ページの書き換えができちゃいます!!

じゃ、早速拡張機能に入れる、前に、本当にできるか試してみましょう。開発者ツールのコンソールにさっきのスクリプトを貼り付けて実行してみます。

image.png

するとこの通り!!!

image.png

ちゃんと難易度の丸も色が変わっていますね!!やったー

よーしこれで晴れて拡張機能にも入れられます。JS の指定はマニフェストに次のように書けば OK です!

manifest.json
{
	"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 です。よーし拡張機能の修正を反映していくぞー、と?

image.png

あれれー丸が変わってなーーい、なんで?コンソールからやったら OK だったのに、うーんなんでー

って感じで、失敗してしまいます。

スクリプトの実行を遅らせる

どうして失敗してしまうんだ!!これはたぶん、この難易度の丸が生成される前に拡張機能のスクリプト実行されてしまうのが原因だと思います。難易度の丸、どう考えても内部スクリプトで作られてるはずなので、実行のタイミングが大事なんですね。

結論からいうと、スクリプトを次のように変更します。

script.js
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;
			}
		}
	}
}

まず、windowaddEventListenerload が発火するのを待ちます。load は HTML や CSS、画像の読み込みが完了すると発火するので、ページの読み込みが終わると、main という関数が呼ばれる感じになっています。

つぎでは setIntervaljsLoaded という関数を 500 ミリ秒ずつ繰り返し処理します。これに jsInitCheckTimer という名前をつけておき、あとでキルするのに使います。

jsLoaded では何をするかというと、さっきまでの問題はおそらくクラス table-problem が完成する前にスクリプトを実行しちゃったので色が変わらなかった、という問題だったと思います。なので、table-problem がしっかりモノを持つまで待ち続ける必要があります。getElementsByClassName は配列が返ってくるので長さが 1 以上になるまで待ちます。

table-problem が完成したら if の中に入ってくれます。もう待つ必要がないので、jsInitCheckTimer は殺しておきます。あとはさっきまでの色置換の処理を行えば OK です!!

改めて変更を反映して、AtCoder Problems のサイトにつないでみると

image.png

1 秒くらい経ったあとにしっかり難易度の丸も色が変わりました!!成し遂げたぜ

おわりに

という感じで、拡張機能を実装しました。CSS の上書きと、要素の置換という 2 つを行っていますが、結構勉強になることも多かったです。

ゆくゆくは上向き矢印もちゃんと変わるようにアプデしたいね。

参考

2. 実践編 - Chrome 拡張機能を作ろう
JavaScript:class要素のHTMLをreplace。
動的なページの読み込みが完了してからChrome拡張機能を実行する方法 #JavaScript - Qiita

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?