トピックを検索
ページではなくサブメニューが開いてしまうドロップダウンの親メニュー
1つのリンクがセクションのページへの移動とサブメニューの開閉を兼ねているため、Enterキーで何が起きるかをスクリプトが決めてしまい、一覧とページのどちらが開くかも伝わらない
event.preventDefault(); 問題
キーボードの利用者はメニューのリンクで目的のページへ移動できるとは限らず、スクリーンリーダーの利用者にはEnterキーで一覧が開くのかページを離れるのかが分からない
家具店のヘッダーに「ホーム」「商品」「会社概要」の三つの項目があります。「商品」は全商品をまとめたページへのリンクで、マウスを重ねると「照明」「椅子」「ラグ」の一覧が下に開きます。マウスの利用者は、一覧へポインターを移してカテゴリーを選ぶことも、「商品」をクリックしてすべての商品を見ることもできます。ポインターは、リンクを押さずにその上で止まっていられるからです。
キーボードには、止まっているという操作がありません。Tabキーで「商品」へ移動してEnterキーを押すと、一覧を開くかページへ移動するかは、サイト側で決めるしかありません。よくある作り方の一つは、1回目と2回目で使い分けるものです。1回目のEnterでは一覧が開いてページはそのまま、2回目でリンク先へ移動します。スクリーンリーダーは押す前も後も「商品、リンク」と読むため、利用者には何も起きなかったように聞こえます。もう一度押すと「商品」のページへ移り、「照明」にはたどり着けません。
もう一つのよくある作り方は、キーボードでメニューを使えるようにするために書かれたものです。Enterキーで一覧が開閉し、リンクは「折りたたみ」「展開」と読まれます。その代わりに、「商品」のページへ行く手段がなくなりました。マウスで「商品」をクリックすればそのページへ移動しますが、キーでは移動できません。スクリーンリーダーを使っていると、逆のことも起きます。ブラウズモードでは、スクリーンリーダーがEnterキーをページに渡さずにリンクを自分でクリックすることが多いため、利用者は「商品」のページへ移動し、一覧を目にすることはありません。
リンクがリンクとして読み上げられるのは、押すとどこかへ移動するからです。ここでは1つのリンクが、ページへの移動と一覧の開閉という二つの役目を持っています。どちらか一方だけを選べるのはポインターだけで、重ねれば一覧が開き、クリックすればページへ移動します。これらの作り方では、押したときにどちらの役目を果たすかをスクリプトが決めており、その決まりはマークアップのどこにも書かれていません。
WCAG 2.1.1 キーボードは、ページでできることをすべてキーボードでもできるよう求めています。二つ目の作り方は、この基準を満たしません。キーのハンドラーが「商品」でのEnterを取り消して一覧を開く一方、マウスのクリックはリンク先へ移動するため、「商品」のページはマウスなら1回のクリックで開けるのに、キーボードでは届きません。一つ目の作り方は、2回目のEnterでページに届くため、2.1.1は満たします。
4.1.2 名前・役割・値は、コントロールの役割と状態を読み取れること、そしてその変化が伝わることを求めています。一つ目の作り方では、「商品」は一覧を開くので、開いているか閉じているかという状態を持ちますが、それを示すものがありません。スクリーンリーダーは一覧が開いていてもいなくても「商品、リンク」と読みます。二つ目の作り方では、aria-expanded で状態は伝わりますが、役割はリンクのままで、そのリンクはEnterキーでリンクとして働きません。「商品、リンク、折りたたみ」から分かるのは何かが開くということだけで、Enterキーではページへ移動できないことは分かりません。また、ブラウズモードのスクリーンリーダーはたいていEnterキーをリンクのクリックに置き換え、キーのハンドラーは動かないため、何が起きるかはスクリーンリーダーを使っているかどうかで変わります。
スマートフォンでタップしたときの動作は、スクリプトだけでなくブラウザーにも左右されます。最初のタップで一覧を開き、2回目でリンク先へ移動するブラウザーもあれば、すぐに移動するブラウザーもあります。同じヘッダーが利用者によって三通りに動き、そのどれもが前もって伝えられません。
10グループ中3グループに影響
- 全盲 (影響あり)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響あり)
- 筋力・可動域 (影響なし)
- 認知 (影響あり)
- 光過敏 (影響なし)
- 手の操作が限られる キーボードやスイッチの利用者は、「商品」のページへ行こうとして「商品」でEnterキーを押します。一つ目の作り方では、ページではなく一覧が予告なく開き、2回目でようやくページへ移動します。二つ目の作り方では、マウスのクリックで開くページに、どのキーでも届きません。
- 視覚を使わない スクリーンリーダーの利用者はリンクと聞いて、新しいページが開くと考えます。一つ目の作り方では、Enterキーを押しても何も起きないように聞こえ、次のEnterでページを離れます。二つ目の作り方では、リンクなのに「折りたたみ」と読まれます。さらにブラウズモードでは、スクリーンリーダーがEnterキーを渡さずにリンクをクリックすることがあるため、「商品」のページへ移動してしまい、一覧は開きません。
- 言語・認知・学習の能力が限られる 「商品」を押すと一覧が開くのかページが開くのか、画面のどこにも示されていません。同じ操作が1回目と2回目で違う動作をするため、覚えにくく、今どこにいるのかを見失いやすくなります。
自動チェックが読むのは <a href="/products"> という、名前のある正しいリンクです。aria-expanded もリンクに付けてよい属性なので、何も指摘されません。Enterキーを押さないため、リンクがどこへも移動しないことや、2回目でしか移動しないことには気づきません。ドロップダウンをキーボードで使えるようにする方法として、リンク先へ移動させずにEnterキーでサブメニューを開くよう勧める記事もあります。AIのコーディングツールに同じことを頼むと、二つ目の作り方そのものを書くことがあります。リンクにキーのハンドラーを付けてEnterを取り消し、一覧を開き、aria-expanded や aria-haspopup を加えるものです。このコードは自動チェックを通りますが、問題を一覧からページへ移しただけです。
この診断ツールは、サブメニューを持ち、横にボタンのない上段の各リンクでEnterキーを押します。その際、リンク先への移動は止め、ページはそのままにします。Enterキーで一覧が開き、リンク先へは一度も移動しない一方、マウスのクリックでは移動する場合は、問題として報告します。1回目のEnterで一覧が開き2回目でリンク先へ移動する場合や、どの操作でもリンク先へ移動しない場合は、確認が必要な項目として報告します。ページが何を伝えているか、「商品」のページがほかの場所からリンクされているかは、人が判断する必要があるからです。どちらの場合も、確かめるにはマウスを使わずにヘッダーをTabキーで移動し、さらにスクリーンリーダーのブラウズモードでも試します。
試してみる
キーボードだけで「商品」のページへ移動し、次に「照明」へ移動してみましょう
問題1: 「商品」でEnterキーを押します。一覧は開きますが、下の欄は「商品、リンク」と読むだけです。もう一度Enterキーを押すと、何の予告もなく「商品」のページへ移動します。
問題2: 「商品」でEnterキーを押すと一覧が開いたり閉じたりするだけで、「商品」のページへは移動できません。マウスで「商品」をクリックすると、すぐに移動します。
修正を適用します。 リンクでEnterキーを押せば必ずそのページへ移動し、一覧は開閉状態を伝えるボタンで開きます。
問題あり 修正できますか?
ホーム
スクリーンリーダーの読み上げ:
(Tabでメニューへ移動)
修正方法
ページにはリンクを、一覧には専用のボタンを用意する
1回目で一覧が開き、2回目でリンク先へ移動する
<nav aria-label="メイン"> <ul class="menu"> <li> <a href="/">ホーム</a> </li> <li class="has-sub"> <a href="/products">商品</a> <ul> <li> <a href="/products/lamps">照明</a> </li> <li> <a href="/products/chairs">椅子</a> </li> <li> <a href="/products/rugs">ラグ</a> </li> </ul> </li> <li> <a href="/about">会社概要</a> </li> </ul></nav>問題点2点
マウスではポインターを重ねた時点で一覧が開くため、クリックすれば必ずリンク先へ移動します。リンクでEnterキーを押したときも同じclickイベントが発生するため、ここで問題になります。
- 「商品」での1回目のEnterは取り消され、代わりに一覧が開きます。
- 「商品」は押す前も後も「商品、リンク」と読まれるため、一覧が開いたことも、次のEnterでページを離れることも伝わりません。
リンクと、その横のボタン
<nav aria-label="メイン"> <ul class="menu"> <li> <a href="/">ホーム</a> </li> <li class="has-sub"> <a href="/products">商品</a> <button type="button" aria-expanded="false" aria-controls="sub-products" aria-label="商品のページ一覧">▾</button> <ul id="sub-products" tabindex="-1" hidden> <li> <a href="/products/lamps">照明</a> </li> <li> <a href="/products/chairs">椅子</a> </li> <li> <a href="/products/rugs">ラグ</a> </li> </ul> </li> <li> <a href="/about">会社概要</a> </li> </ul></nav>修正の手順6ステップ
- 「商品」はハンドラーのないただのリンクに戻します。Enter、クリック、タップのどれでも、1回目で「商品」のページへ移動します。
- その直後に、シェブロンを描いたHTML標準のbuttonを置きます。Enter、Space、クリックで一覧を開閉できます。
- buttonには「商品のページ一覧」という名前を付け、スクリーンリーダーや音声操作でリンクと区別できるようにします。大きさは24×24ピクセル以上にします。
- buttonに
hiddenと連動するaria-expandedを付けます。「商品のページ一覧、ボタン、折りたたみ」、開くと「展開」と読み上げられます。aria-controlsで一覧と結び付けます。 - Escapeで一覧を閉じてフォーカスをbuttonに戻し、フォーカスが項目から離れたら、フォーカスを動かさずに閉じます。
- 一覧に
tabindex="-1"を付けます。一覧の中をクリックしてもフォーカスが項目の内側に残り、離れたとは見なされません。
これで二つの修正前の例はどちらも直り、2.1.1と4.1.2を満たします。各コントロールが一つの役目だけを持ち、それを伝えるからです。W3CのAPGにある、上段のリンクを残したナビゲーションの例(Disclosure Navigation Menu with Top-Level Links)と同じ形です。WordPressのナビゲーションブロックも、サブメニューをマウスを重ねて開く設定にし、矢印を表示していれば、この形で出力されます。
この方法を選ぶ理由:まずHTMLの要素を使う
どちらも基準を満たします。お店のカテゴリーや会社概要のように、セクションのページそのものに意味がある場合は、リンクを残してボタンを加えます。誰にとっても1回のクリック、タップ、Enterで届くページのままで、W3Cの例やWordPressのナビゲーションブロックもこの形です。その代わりに項目ごとに小さめの操作対象がもう一つ増え、それには専用の名前と十分な大きさが必要です。項目のページが一覧の中身の目次にすぎない場合は、項目をボタンにします。項目ごとにコントロールが一つ、Tabの停止位置も一つで、押しやすい大きさになり、「商品」のページへのリンクは一覧の先頭に入ります。
ボタンには専用の名前が必要です。リンクと同じ名前にすると、「商品、リンク」の次に「商品、ボタン」と、同じものが2回あるように聞こえ、音声操作で「商品をクリック」と言った利用者は二つの候補から選ぶことになります。「商品のページ一覧」のように、何が開くかを表す名前にします。シェブロンはリンクの文字の中ではなくボタンの中に描き、一覧を開く操作対象にします。リンクの文字の中に描くと、それを押した利用者はページへ移動してしまいます。
実際のメニューでは、ほかの部分についても判断が必要です。
- マウスを重ねて開く操作:マウス利用者のために残してもかまいません。その場合は同じ
setOpenで状態を設定し、一覧が表示されている間はaria-expandedをtrueに保ちます。また1.4.13に従い、ポインターを一覧へ移しても開いたままにし、Escapeで閉じます。詳しくは近づくと消え、閉じられないツールチップを参照してください。 - キーのハンドラー:リンクの
keydownハンドラーで一覧を開かないでください。ブラウズモードのスクリーンリーダーはキーではなくクリックを送ることが多いため、ハンドラーが動く利用者と動かない利用者が出てしまいます。 aria-haspopup:リンクにもボタンにも付けません。メニューとして読み上げられますが、この一覧はリンクの集まりで、メニューではありません。- ターゲットサイズ:シェブロンのボタンは小さくなりがちです。24×24ピクセル未満なら周りに余白が必要です。ターゲットサイズが足りない、詰まったボタンで説明しています。
- スマートフォン:狭い画面のメニューでも同じリンクとボタンが使え、一覧は縦に並んで次の項目を押し下げます。狭い画面だけ、最初のタップで一覧を開く決まりに切り替えることはしません。
デザインの案
1つのメニュー項目でも、ページへの移動と一覧の表示を両立できる
「商品」の描き方を4通り並べました。マウスを使わずに、Tabキーでそれぞれを移動してみてください。言葉の上でEnterキーを押すとそのページへ、シェブロンや+の上で押すと一覧が開きます。3つはどちらもキーボードで使えます。最後の1つは、ポインターでしか一覧が開きません。
デザインファイルで
- 「商品」という言葉を押したときの動作を、ページへの移動か一覧の表示か、どちらか1つに決める。
- 言葉でページへ移動するなら、シェブロンや+は線か余白で区切り、別の押す場所として描く。
- 言葉で一覧を開くなら、「商品」のページを一覧の先頭に、目に付くように入れる。
- シェブロンには「商品のページ一覧」のような名前をデザインファイルに書き、隣のリンクと区別できるようにする。
- 開いた状態も描き、シェブロンは上向きに、+は-にする。
合格
- 線で2つに分けた1つのピル 「商品」とシェブロンを1つの枠線付きのピルに収め、1pxの線で区切ります。1つの項目に2つの端があるように見え、言葉はページへ、シェブロンは一覧を開いて上を向きます。左右はそれぞれのホバーで塗られ、フォーカスリングもそれぞれに付きます。文字は白地に#1a1a1a(17.40:1)です。
基本原則:まずHTMLの要素を使う
- 先頭に「すべての商品」を置いたパネル 「商品」はパネルを開くだけにし、以前開いていたページは、パネルの先頭でいちばん大きなリンクにします。「すべての商品」と書いた色付きのカード(#f5f2ecに#1a1a1a、15.58:1)です。ヘッダーのTabの移動先は1つのままで、一覧ページへは1回押す操作が増えますが、見落とされない場所にあります。
基本原則:まずHTMLの要素を使う
- スマートフォンのメニュー:行と、専用の+ スマートフォンのメニューでは、「商品」の行を2つに分けます。言葉はページへ移動し、右端の線で区切った正方形の+は、下に「照明」「椅子」「ラグ」を開いて-に変わります。言葉を1回押せば必ずページへ移動するため、どうなるかを推測する必要がありません。
基本原則:まずHTMLの要素を使う
不合格
- スマートフォンのメニュー:リンクの中に描いた+WCAG 2.1.1 見た目は上の行と同じですが、行全体が1つのリンクで、中の+はポインターが触れたときだけ一覧を開きます。Tabキーは「商品」に1回止まるだけで、Enterキーを押すとページへ移動し、「照明」「椅子」「ラグ」はキーボードでは開きません。
修正の確認
5項目、マウス不要
確認方法: キーボード、スクリーンリーダー、タッチ、拡大表示
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。共通のヘッダーやメニューのコンポーネントを変更した際は、再度確認します。
スクリーンリーダーでの aria-expandedの読まれ方
制限
このガイドで扱うのは、リンクでありながらサブメニューも開く上段の項目です。スクリプトもボタンもなくマウスを重ねたときだけ開くサブメニューや、一覧が開いているかどうかを伝えないボタンは、マウスでしか開かないか、開閉が伝わらないドロップダウンメニューで扱っています。コマンドを並べるアプリケーションのメニューは、代わりにARIAのメニューパターンに従います。スクリプトは、アニメーション、マウスを重ねて開く操作、入れ子の階層を含まない最小限の例です。狭い画面のメニューも含めて、実際に使用する支援技術で操作全体を確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
EaseWebのサービス
修正した問題を、次のリリースで再発させない
このガイドのような修正方法を、コーディングエージェントが従うルールと、CIのチェックに組み込みます。あわせて定期的に、キーボードとスクリーンリーダーで手動の再テストを行います。