つくる ダークモード切替

このレッスンで分かること

  • classList.toggle は、付いていれば外し、付いていなければ付けます
  • toggle の戻り値は「実行後に付いているか」の真偽値で、そのまま表示の切り替えに使えます
  • 見た目の切り替えは CSS に任せ、JavaScript は class の付け外しだけを担当します

つくる ダークモード切替 とは

クリックイベントと classList を組み合わせて、ページの配色を明暗で切り替える機能です。

ここまでで手に入れたもの

第8章では、要素を取ってくる querySelector、文字を書き換える textContent、class を付け外しする classList を学びました。この章に入ってからは、addEventListener で処理を出来事に結び付け、event オブジェクトからどれが押されたかを読み取り、入力欄の値を扱えるようになりました。

ここまでの部品を組み合わせると、もう「押したら見た目が変わる」がつくれます。今回はダークモードの切り替えボタンです。押すたびに配色が反転し、もう一度押すと元に戻ります。

要点は、JavaScript が色を直接いじらないことです。style.backgroundColor に色を代入していく書き方でも動きますが、色の数だけ行が増え、どこで何色にしたのか追えなくなります。かわりに、JavaScript は bodydark という class を付けたり外したりするだけにします。その class が付いたときの配色は CSS 側にすでに書いてあります。

body.dark { background-color: #1b1f27; color: #e8eaee; }

役割を分けておくと、あとから配色を変えたくなっても CSS だけを直せば済みます。JavaScript は一切さわりません。この分け方は、ダークモードにかぎらず「開いている」「選ばれている」「読み込み中」といった状態の表現すべてに使えます。

classList の 4 つのメソッド

第8章で出てきた classList をおさらいします。

メソッド動き戻り値
add付ける。すでにあれば何もしないなし
remove外す。無ければ何もしないなし
toggleあれば外し、無ければ付ける実行後に付いているか
contains付いているかを調べるtrue か false

切り替えボタンは、addremove を自分で振り分けても書けます。

if (document.body.classList.contains("dark")) { document.body.classList.remove("dark"); } else { document.body.classList.add("dark"); }

間違ってはいませんが、これとまったく同じことを 1 行で書けるのが toggle です。

document.body.classList.toggle("dark");

条件分岐が消えるぶん、読み違いも起きません。切り替えを書くときは toggle を第一候補にしてください。

toggle は実行後の状態を真偽値で返します。付いた直後なら true、外れた直後なら false です。この戻り値をそのまま使うと、ボタンの文言もいっしょに切り替えられます。もう一度 contains で調べ直す必要はありません。

body を取る

body は特別な要素で、document.body と書けばいつでも取れます。document.querySelector("body") でも同じものが返りますが、document.body のほうが短く、意図もはっきりします。

const themeToggle = document.querySelector("#theme-toggle"); themeToggle.addEventListener("click", function () { const isDark = document.body.classList.toggle("dark"); themeToggle.textContent = isDark ? "ライトモード" : "ダークモード"; });

第3章で学んだ三項演算子が、ここで短く効いています。isDarktrue なら次に押すとライトに戻るので、ボタンには行き先である ライトモード を出します。ボタンの文言を「いまの状態」にするか「押したらどうなるか」にするかは設計の分かれ目ですが、行き先を書くほうが押す前に迷いません。

なぜ body に付けるのか

class を付ける相手は、変えたい範囲のいちばん外側にします。ページ全体の配色を変えるなら body です。CSS の子孫セレクタを使えば、body.dark .section のように中の要素までまとめて指定できます。

body.dark .section { background-color: #242a34; border-color: #333a46; }

もし .section それぞれに class を付けて回ると、要素の数だけ処理が増え、あとから追加した要素には付きません。外側 1 か所に付けて中は CSS で受ける、という形にしておくと、JavaScript は 1 行のままで済みます。

状態を class で表す、という型

今回覚えてほしいのは、ダークモードの作り方そのものよりも、その裏にある型です。画面の状態は class の名前で表し、その名前が付いたときの見た目は CSS に書く。JavaScript は名前を付け外しするだけ。この 3 行が守られていれば、機能が増えても JavaScript は太りません。

現場でよく見る class 名を挙げると次のようになります。

class 名表している状態
is-openメニューやパネルが開いている
is-activeいま選ばれている項目
is-loading読み込み中で操作させたくない
has-error入力に問題がある

is-has- を先頭に付けるのは、見た目のための class と区別するためです。.card は形を決める class、.is-active は状態を表す class、と読み分けられます。次の回では has-error を実際に使います。

もう一点、切り替えたことをブラウザに伝えたいなら aria-pressed などの属性もいっしょに変えます。読み上げソフトを使う人に、いま押されている状態かどうかが伝わります。今回は必須にしませんが、覚えておくと役に立ちます。

今回書くこと

ヘッダーの中に、id="theme-toggle" のボタンを足します。class="theme-toggle"type="button" も付けてください。見た目の CSS は用意してあります。

JavaScript では、そのボタンに click を登録し、document.body.classList.toggle("dark") を呼びます。戻り値を受け取って、ボタンの文字も ライトモードダークモード で切り替えます。

動きを確かめるときは、必ず 2 回押してください。1 回押して暗くなっても、もう一度押して明るく戻らなければ切り替えになっていません。往復して初めて完成です。

よくある間違い

add だけ書いている

themeToggle.addEventListener("click", function () { document.body.classList.add("dark"); });

1 回目は暗くなりますが、2 回目以降は何も起きません。add はすでに付いていれば黙って何もしないからです。toggle に置き換えます。

class 名にドットを付けてしまう

document.body.classList.toggle(".dark");

classList に渡すのは class 名そのものです。ドットが要るのは querySelector のセレクタのときだけです。.dark という名前の class が付いてしまい、CSS には当たりません。

className を上書きしてしまう

document.body.className = "dark";

これは class 全体を dark だけで置き換える書き方です。元から付いていた class があれば消えてしまいます。付け外しには classList を使います。

課題

  1. HTML の .site-header の中、nav のうしろに id="theme-toggle" class="theme-toggle" type="button" のボタンを足し、文字は「ダークモード」にする
  2. JavaScript で #theme-toggleclick を登録する
  3. 処理の中で document.body.classList.toggle("dark") を呼ぶ
  4. toggle の戻り値を使って、ボタンの文字を「ライトモード」と「ダークモード」で切り替える

ヒント

index.html
style.css
script.js
プレビュー

できているか

  • #theme-toggle のボタンがある
  • 切替ボタンがヘッダーの中にある
  • 最初はライトモードになっている
  • 2 回押すと元のライトモードに戻る
  • もう一度押すとダークモードになる