フォーム・入力
チェックボックス
解説ありCheckbox
オン/オフの選択。中間状態(mixed)を含むチェックボックスの扱いも。
チェックボックスとは?
チェックボックスは、項目を「オン / オフ」で選ぶ UI です。 設定の有効化、利用規約への同意、複数項目の選択(通知の種類、絞り込み条件など)でよく使われます。
さらに「すべて選択」のような親チェックボックスでは、 子が「全部オン」「全部オフ」だけでなく「一部だけオン」という第3の状態 (中間状態 / mixed)を表す必要があります。ここが見落とされがちなポイントです。
なぜアクセシビリティが大事なの?
- キーボードだけで操作する人。ネイティブの
<input type="checkbox">なら Tab でフォーカスでき、Space で切り替えられます。<div>で作ると、そもそも操作できません。 - スクリーンリーダーを使う人。
<label>と関連付いていれば 「メール, チェックボックス, オフ」のように名前・役割・状態が読み上げられます。 関連付けがないと、何のチェックなのか分かりません。
最短ルートはネイティブの <input type="checkbox"> を<label> と結びつけること。中間状態は indeterminate を使います。
ライブデモ(推奨実装)
「すべて選択」と3つの子チェックボックスです。子を一部だけオンにすると、親が中間状態(チェックでもなく空でもない見た目)になります。
試してみよう:Tab で各チェックボックスへ → Space でオン/オフ。子を1つだけオンにすると親が中間状態に、全部オンにすると親もオンになります。
ポイント
中間状態のとき、スクリーンリーダーは「すべて選択」を 「一部選択 / mixed(半選択)」と読み上げます。これはindeterminate = true を設定すると、ブラウザが自動的にaria-checked="mixed" として支援技術へ伝えてくれるためです。
キーボード操作
| キー | 動作 | 必須/任意 |
|---|---|---|
| Tab | 次のチェックボックスへフォーカス移動 | 必須 |
| Space | フォーカス中のチェックボックスをオン/オフ | 必須 |
補足
Space での切り替えは <input type="checkbox"> を使うだけで自動的に得られます。自分でキー処理を書く必要はありません。
必要な WAI-ARIA / ロール
| 付ける場所 | 属性 / ロール | 意味 |
|---|---|---|
| 入力欄 | <input type="checkbox"> | ネイティブの checkbox ロール・状態・キーボード操作がすべて自動で付く。 |
| ラベル | <label>(input を内包 or for で関連付け) | チェックボックスの「名前」になる。ラベルのクリックでもトグルできる。 |
| 親(すべて選択) | indeterminate = true(JSプロパティ) | 一部だけ選択の中間状態。aria-checked="mixed" として自動で伝わる。 |
| グループ | <fieldset> + <legend> | 関連するチェックボックスをまとめ、グループ名を与える(任意・推奨)。 |
補足
indeterminate はHTML属性ではなく JavaScript のプロパティです (el.indeterminate = true)。見た目が中間でも、内部の checked 値はtrue/false のままなので、送信値の扱いに注意します。
実装:推奨パターン(Good)
良い例 / 推奨
ネイティブの <input type="checkbox"> を <label> と関連付け、親は indeterminate で中間状態を表します。
マークアップ:
<fieldset>
<legend>受け取る通知</legend>
<!-- 親:すべて選択(中間状態は indeterminate で表現) -->
<label>
<input type="checkbox" id="cb-all"> すべて選択
</label>
<!-- 子:ネイティブの input + label を関連付け(Space でトグル) -->
<label><input type="checkbox" class="cb-child"> メール</label>
<label><input type="checkbox" class="cb-child"> SMS</label>
<label><input type="checkbox" class="cb-child"> アプリ通知</label>
</fieldset>「すべて選択」と中間状態の同期:
const all = document.getElementById('cb-all');
const children = document.querySelectorAll('.cb-child');
// 親 → 子:まとめて切り替える
all.addEventListener('change', () => {
children.forEach((c) => { c.checked = all.checked; });
all.indeterminate = false; // チェックが揃ったので中間状態を解除
});
// 子 → 親:全部 / 一部 / ゼロで親の状態を出し分ける
children.forEach((c) => c.addEventListener('change', () => {
const checked = [...children].filter((x) => x.checked).length;
all.checked = checked === children.length; // 全部 → on
all.indeterminate = checked > 0 && checked < children.length; // 一部 → mixed
}));アンチパターン(Bad)
下は <div> で作った「見た目だけ」のチェックボックスです。マウスでは切り替わりますが、キーボードでは操作できず、状態も読み上げられません。
試してみよう:Tab を押してもフォーカスが当たりません。スクリーンリーダーでは「チェックボックス」とも「オン/オフ」とも伝わりません。
<!-- ❌ アンチパターン:div で作った"チェックボックス風" -->
<div class="checkbox" onclick="this.classList.toggle('on')">
<span class="box"></span> メール
</div>悪い例 / 避ける
この実装の問題点:
- キーボードで操作できない —
divはフォーカスを受け取れない。 - 役割が伝わらない — 「チェックボックス」だと認識されない。
- 状態が伝わらない — オン/オフ(や中間状態)が読み上げられない。
- ラベル未関連 — 文字をクリックしても切り替わらず、名前も結びつかない。
実装チェックリスト
- チェックボックスは
<input type="checkbox">である <label>と関連付いている(内包 orfor/id)- Space でトグルでき、フォーカスが見える
- 「すべて選択」の一部選択は
indeterminate = trueで表す - 親 ↔ 子の状態が
changeで常に同期している - 関連グループは
<fieldset>+<legend>でまとめる(任意・推奨)