トピックを検索
読み上げられないSPAの画面遷移
シングルページアプリのルーターが新しい画面を表示してタイトルも変えますが、フォーカスは元の場所のままです。何も読み上げられず、次のTabは元の位置から進みます。
問題
リンクで画面が切り替わっても、スクリーンリーダーの利用者には何も聞こえず、キーボードの利用者はもう意味のない位置から操作を続けることになります。
お茶の通販サイトがシングルページアプリで作られています。ヘッダーには「紅茶・緑茶」「茶器」「かご」の三つのリンクがあります。キーボードの利用者が「茶器」でEnterを押すと、アドレスが /teaware に変わり、タブのタイトルが「茶器 – Harbor Tea」になり、お茶の一覧が急須の一覧に入れ替わります。マウスを使う人には、すべて見えています。しかしスクリーンリーダーの利用者には何も聞こえません。フォーカスは「茶器」のリンクに残ったままで、何かが変わったことをスクリーンリーダーに伝えるものがないからです。リンクが効いていないのかと思い、もう一度Enterを押します。
「紅茶・緑茶」の画面の最後には「茶器を見る」というリンクもあります。こちらを押すと、さらに困ったことになります。リンクは古い画面の一部なので、画面と一緒に削除され、フォーカスは文書そのものに落ちます。次のTabは、ブラウザーによってページの先頭から、または削除されたリンクがあったあたりから始まります。
普通のウェブサイトでは、リンクをたどると新しいページが読み込まれます。ブラウザーはフォーカスを文書の先頭に戻し、スクリーンリーダーは新しいページのタイトルを読み上げます。シングルページアプリでは新しいページを読み込みません。ルーターがデータを取得してその場で内容を入れ替えるため、ページの読み込みが自然にしていたことは、すべてアプリの仕事になります。この例のルーターはタイトルを設定するだけです。document.title の変更はスクリーンリーダーに読み上げられないため、画面を見ていない人には変化が伝わりません。
関係する達成基準は二つです。2.4.3「フォーカス順序」は、ページの意味と操作性を保つ順序でフォーカスが移ることを求めています。すでに表示された画面へのリンクにフォーカスが残ったり、要素が消えて文書に落ちたりすると、次のTabは今表示されている内容と関係のない場所へ進みます。4.1.3「ステータスメッセージ」は、フォーカスを移さずに操作の結果を伝える変化を、支援技術に伝えられることを求めています。新しい画面はまさにその結果ですが、誰にも伝わっていません。
マウスで確認する人には、画面が期待どおりに動くため、見落とされやすい問題です。画面全体を見ずに、またはポインターを使わずに操作する人にだけ現れます。
10グループ中3グループに影響
スキャンツールはURLを一つずつ読み込むため、確認する画面はどれもページの読み込みで表示されたもので、合格になります。問題があるのは画面から画面への移動の間だけで、ページ単位のチェックには見えません。EaseWebの診断は各ページでナビゲーションのリンクを一つ押し、ページを読み込まずにアドレスが変わり、フォーカスが動かず、ライブリージョンも何も伝えなかった場合に、確認候補として報告します。使えるかどうかの判断は人が行います。AIが書くコードは、document.title の設定で終わることが多く、それで十分に見えます。画面全体に aria-live を付ける例もありますが、ページ全体を読み上げるスクリーンリーダーもあれば、何も読まないものもあります。キーボードとスクリーンリーダーで実際にリンクをたどって、初めて分かります。
修正方法
画面遷移のたびに、新しい画面の見出しへ、またはページ名を読み上げたうえでページの先頭へ、フォーカスを移します。
- 1画面遷移のたびに、フォーカスを意図して移す。新しい見出しへ移すか、読み上げを添えて先頭へ戻す。
- 2新しい画面の名前を一度伝える。フォーカスの移動か、最初からあるステータス領域で伝える。
- 3最初の読み込み時と、一つの画面の中での変化では、フォーカスを動かさない。
<body> <nav> <a href="/teas">紅茶・緑茶</a> <a href="/teaware">茶器</a> <a href="/basket">かご</a> </nav> <main id="view"> <h1>茶器</h1> … </main></body>新しい画面の見出しにフォーカスを移す
<body> <nav> <a href="/teas">紅茶・緑茶</a> <a href="/teaware">茶器</a> <a href="/basket">かご</a> </nav> <main id="view"> <h1 tabindex="-1">茶器</h1> … </main></body>タブのタイトルは正しくなりますが、スクリーンリーダーはタイトルの変更を読み上げません。フォーカスは押したリンクに残るか、そのリンクが古い画面と一緒に消えた場合は文書に落ちます。
見出しにフォーカスを移すと、二つの役目を一度に果たせます。スクリーンリーダーはフォーカスを受けた見出しを読み上げ(「茶器、見出しレベル1」)、次のTabは新しい画面の最初のリンクやボタンへ進みます。tabindex="-1" があれば、Tabの順序には加えずに、スクリプトから見出しにフォーカスを移せます。最初の読み込みでは、ブラウザーがすでにフォーカスをページの先頭に置いているため、ルーターは何もしません。
どちらも2.4.3と4.1.3を満たします。一つ目は新しい画面の名前を持つ要素へフォーカスを移すことで、二つ目はフォーカスを戻して名前をステータスメッセージで伝えることで満たします。見出しへの移動は利用者を新しい内容へ直接連れていき、Gatsbyのユーザーテストではスクリーンリーダーの利用者に最も分かりやすい方法でした。先頭へ戻す方法は、大きなアプリでは通り抜ける量が多く感じられたと報告されています。一方で先頭へ戻す方法は、普通のページ読み込みと同じ感覚で使え、すべての画面に見出しがなくても動きます。フレームワークがすでに行っている場合もあります。SvelteKitはフォーカスを body に移し、自前のステータス領域で新しいタイトルを読み上げます。フレームワークが先頭へ戻すなら、それを活かし、各タイトルが画面を表しているかを確認してください。ルーティングを自分で書くなら、見出しへの移動のほうが小さな変更で済みます。
URLが変わっても、新しい画面とは限りません。 絞り込み、並べ替え、検索結果のページ送り、タブも、アドレスを書き換えることがよくあります。そこでフォーカスを動かすと、操作中の部品から利用者を引き離してしまいます。フォーカスはそのままにして、件数が読み上げられない検索・絞り込み結果のように結果を伝えます。フォーカスを移すのは、メインの内容が入れ替わったときだけです。
囲みではなく見出しにフォーカスを移します。 main や画面の囲みにフォーカスを移すアプリもあります。その場合、スクリーンリーダーによって「メイン」とだけ読んだり、画面全体を一度に読んだりします。h1 なら、短く決まった読み上げになります。この修正にはすべての画面に見出しが必要ですが、もともとあるべきものです。
フォーカスリングは残します。 フォーカスを受けた見出しには、ブラウザーのフォーカスリングが出ることがあります。消さないでください。画面拡大ソフトの利用者は、それを頼りに移動先を見つけます。見出し用に控えめなリングにするのは構いません。フォーカスした要素は画面内にスクロールし、戻るの操作では、ルーターが保存したスクロール位置を復元します。
戻る・進む。 リンクと同じように扱います。画面を離れたときのリンクにフォーカスを戻すアプリもあり、良い追加ですが、代わりにはなりません。
後から届くデータ。 画面が先に読み込み中の表示を出す場合は、見出しが表示されてからフォーカスを移すか、読み上げます。待つ間は、読み上げられない読み込み中の表示のように、読み込み中であることをステータス領域で伝えます。
それだけでは修正にならないもの。
document.titleの変更:タブは正しくなりますが、何も読み上げられません。すべてのページで同じページタイトルを防ぐために、必要ではあります。- 画面全体への
aria-live:すべてを読むスクリーンリーダーもあれば、何も読まないものもあり、変わるたびに全体が繰り返されます。 - フォーカスを残したままのステータス領域:名前は読み上げられ、ナビゲーションのリンクにフォーカスが残るのも不自然ではありません。しかし押したリンクが古い画面の一部だった場合、フォーカスはリンクと一緒に失われます。
- 見出しへの
autofocus:ブラウザーが適用するのは文書ごとに一度だけで、画面遷移のたびではありません。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得
修正の確認
マウスを使わない5つの確認
確認方法: キーボード、スクリーンリーダー、ズーム
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。ルーターやレイアウトを変更した際は、再度確認します。
制限
これは出発点となる実装例で、すべてのアプリケーションでの動作を保証するものではありません。見出しへのフォーカスと body へのフォーカスへの反応はスクリーンリーダーによって異なるため、利用者が使うもので確認してください。このガイドは、メインの内容を入れ替える画面を扱っています。ダイアログ、ドロワー、ページ内の更新には、それぞれのパターンがあります。ディープリンクや遅い通信も含め、実際のルートで一連の流れを確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。