リストの「選択」を極める:aria-selected を用いたアクセシブルな状態管理のベストプラクティス
現場でフロントエンドを書いていると、「ただのリスト」に機能を持たせる場面に多々遭遇します。ドロップダウンの選択肢、タブ切り替え、あるいは複数選択可能なチェックリスト。これらを実装する際、`class=”is-selected”` を付与して終わりにしていないでしょうか?
もしあなたが「動けばいい」以上のクオリティを求めているなら、WAI-ARIAの仕様に立ち返る必要があります。特に `aria-selected` を適切に扱うことは、アクセシビリティの向上だけでなく、UIの状態をプログラムから「解釈可能」にするという、堅牢なフロントエンド開発の第一歩です。
今日は、中級エンジニアが避けては通れない「リストアイテムの選択状態管理」について、ブラウザの裏側まで含めて深掘りしていきます。
—
なぜ `aria-selected` が必要なのか?
まず前提として、HTMLの標準要素である `
- ` や `
- ` には、デフォルトでは「選択状態」という概念はありません。単なる箇条書きのコンテナに過ぎないからです。
ここに `aria-selected=”true/false”` を付与することで、支援技術(スクリーンリーダーなど)に対して、「この項目は現在選択されており、対話的な操作が可能である」という強いメタデータを伝えることができます。
ブラウザのアクセシビリティツリー上で、この属性がどう振る舞うかを理解しておきましょう。`aria-selected` を検知したブラウザは、要素の役割(Role)に基づいて、「選択されている項目がリストの中にいくつあるか」を計算し、ユーザーに読み上げます。これを怠ると、視覚障害を持つユーザーは「今、自分がどこを選んでいるのか」を正確に把握できなくなります。
—
実践:堅牢な単一/複数選択リストの実装
現場でそのまま使える、状態管理の基本パターンを紹介します。ポイントは、DOMの状態とJavaScriptの論理モデルを分離しつつ、確実に属性を同期させることです。
/
- 選択状態を管理するクラス(シンプルかつ拡張性重視)
/
class SelectionManager {
constructor(containerElement, isMultiple = false) {
this.container = containerElement;
this.isMultiple = isMultiple;
this.items = Array.from(this.container.querySelectorAll(‘[role=”option”]’));this.init();
}init() {
this.container.addEventListener(‘click’, (e) => {
const target = e.target.closest(‘[role=”option”]’);
if (!target) return;this.toggleSelection(target);
});
}toggleSelection(target) {
const isSelected = target.getAttribute(‘aria-selected’) === ‘true’;if (!this.isMultiple) {
// 単一選択の場合は、他のすべての選択を解除する
this.items.forEach(item => item.setAttribute(‘aria-selected’, ‘false’));
target.setAttribute(‘aria-selected’, ‘true’);
} else {
// 複数選択の場合は、トグルする
target.setAttribute(‘aria-selected’, (!isSelected).toString());
}
}
}// 使い方:
//-
//
- Option 1
- Option 2
//
//
注意すべきポイント
- `role=”option”` の指定: `aria-selected` は、`listbox` などの適切なコンテナ(`role=”listbox”`)の配下にある `role=”option”` に対して適用するのが仕様上の正解です。
- キーボード操作: 上記コードはクリックイベントのみですが、実務では `ArrowDown`/`ArrowUp` によるフォーカス移動と `Space`/`Enter` による選択確定の実装が必須です。これができて初めて「プロのUI」と言えます。
—
現場のリアル:状態の「同期」でハマらないために
ここでよくある罠が、「JavaScriptで状態を更新したのに、スタイルが追従しない」、あるいはその逆です。
CSS側では、クラス名ではなく属性セレクタを活用しましょう。これが最も宣言的でミスが少ない書き方です。
/ 属性セレクタをCSSのフックにする /
[role=”option”][aria-selected=”true”] {
background-color: #007bff;
color: #ffffff;
font-weight: bold;
}/ 状態が切り替わった時の微細なアニメーションも属性で制御 /
[role=”option”] {
transition: background-color 0.2s ease;
}このように実装すれば、CSSとJavaScriptの結合度を最小限に保てます。「JavaScriptでクラスをつけ外しする」という泥臭いDOM操作から卒業し、「データ(属性)を更新すれば見た目は自動的に追従する」というアーキテクチャに切り替えるのが、中級から上級へステップアップするための秘訣です。
—
最後に:完璧なUIを目指すあなたへ
`aria-selected` は単なる「装飾」ではありません。ブラウザというOSに対する、開発者からの「この要素はこういう役割で、今こうなっている」という意思表示です。
複雑なフレームワーク(ReactやVueなど)を使っていても、結局ブラウザが解釈するのはこういった標準的な属性です。フレームワークの抽象化レイヤーの裏側で、ブラウザが何をどう処理しているのか。その視点を持ち続けるだけで、デバッグの質とスピードは劇的に変わります。
ぜひ、次回のリスト実装では、`aria-selected` を使ったアクセシブルな設計に挑戦してみてください。あなたの書くコードが、あらゆるユーザーにとってより使いやすいものになることを願っています。

コメント