トピックを検索
読み上げられない読み込み中の表示
スピナーと「読み込み中…」が画面に出ても、処理中であることや結果が届いたことがスクリーンリーダーに伝わりません。
問題
スクリーンリーダーの利用者はボタンを押しても何も聞こえず、うまくいったのか、処理中なのか、失敗したのか分かりません。
アカウントページで「注文を表示」ボタンを押すと、過去の注文を取得します。スピナーが回り、ボタンの下に「注文を読み込み中…」と表示されます。1秒ほどでスピナーが消え、注文が3件表示されます。画面が見える人には流れが分かります。しかしスクリーンリーダーの利用者には、始まりも終わりも何も聞こえません。フォーカスはボタンにあるままなので、もう一度押すか、注文に気づかずに先へ進んでしまいます。
「注文を読み込み中…」や「注文を3件読み込みました」はステータスメッセージです。フォーカスを移さずに、操作の進み具合や結果を伝えます。達成基準4.1.3は、こうしたメッセージをフォーカスなしで支援技術に伝えられることを求めています。この例では文言をただの div に入れているため、画面には出ても読み上げられません。スピナーは文字のない span です。
スピナーは見ている人に向けて描かれるため、見ていない人には何かが起きたことが伝わりません。
10グループ中2グループに影響
読み込み後のページを調べるスキャンは、注文の一覧を見て合格にします。その間の処理中の状態は見ていません。多くのツールは、ステータスメッセージとほかの文言を区別できません。AIが書くコードは、文言を入れた状態で作った要素に aria-live を付けがちですが、内容と一緒に現れた領域は読み上げないスクリーンリーダーが多くあります。aria-busy を付ければ「読み込み中」と伝わると考える例もありますが、そうはなりません。読み込みの最中にスクリーンリーダーで聞いて、初めて分かります。
試してみる
「注文を表示」でEnterキーを押し、読み上げを聞いてください。
下の欄に、読み込み中のスクリーンリーダーの読み上げが出ます。待っていることが言葉で伝わりますか?
問題あり 直してみませんか?
スクリーンリーダーの読み上げ: (「注文を表示」を押してください)
再現デモです。「問題あり」は意図的にアクセシブルでない状態にしています。
修正方法
読み込み中と完了の文言を、リクエスト前からあるライブリージョンに書き込みます。
- 1ライブリージョンは空のままページと一緒に表示し、文言だけを変える。
- 2開始、結果、エラーを言葉で伝える。
- 3フォーカスは動かさない。移動で読み上げさせない。
<button type="button" id="show-orders">注文を表示</button><div id="orders-state"></div><ul id="orders"></ul>文言は画面に出て消えますが、変わったことは支援技術に伝わりません。結果は言葉にすらなっていません。
role="status" の p は最初から空のままHTMLにあるため、文言が変わる時点でブラウザーはすでにこの領域を見ています。role="status" は控えめなライブリージョンです。スクリーンリーダーは読み上げ中の内容を終えてから「注文を読み込み中…」と読み、続いて「注文を3件読み込みました」と読みます。フォーカスはボタンのままです。スピナーはCSSで描くか、aria-hidden="true" を付けます。意味を伝えるのは文言です。
どちらの領域も、フォーカスを動かさずに読み込み中と結果を伝えます。ステータス領域を使ってください。読み込み中と完了は通常の知らせであり、role="status" なら同じコードで、誰の読み上げも遮らずに伝えられます。アラート領域が合うのは、知らせを待たせられないときだけです。一つの操作から両方の種類の知らせが出る場合は、領域を一つずつ用意し、通常の進み具合はステータス領域に、失敗はアラート領域に入れます。
領域を先に用意します。 ページと一緒に空の領域を表示し、文言だけを変えます。メッセージと一緒に追加した領域は、読み上げられないことがよくあります。コンポーネントのフレームワークでは、読み込み中の状態で表示されるブロックの外に領域を置きます。
役割そのものがライブリージョンです。 role="status" は aria-live="polite" と aria-atomic="true" を、role="alert" は aria-live="assertive" を含んでいます。そのため、隣に aria-live を書く必要はありません。とても古いスクリーンリーダーに備えて同じ値の aria-live を添えるチームもあり、害はありません。ただし、role="status" aria-live="assertive" のように、役割と違う強さを組み合わせないでください。役割のない <div aria-live="polite" aria-atomic="true"> でも同じように伝わります。aria-atomic がないと、変わった部分だけを読むスクリーンリーダーがあります。
一度だけ伝えます。 進み具合のパーセントを毎回領域に書き込まないでください。数字が延々と読み上げられます。開始を伝え、数秒かかるなら「まだ読み込んでいます」と一度伝え、最後に結果を伝えます。進捗バーを見せる場合は、領域の外に <progress> を置きます。
短い読み込み。 一瞬で結果が届くなら、「読み込み中」を省いて結果だけを伝えても構いません。同じ文言を続けて書き込むと、読み上げないスクリーンリーダーがあります。いったん領域を空にするか、文言を変えます。
それだけでは修正にならないもの。 aria-busy="true" は、領域の読み上げを待つよう支援技術に伝えるだけで、「読み込み中」とは言いません。新しい内容へフォーカスを移す方法は、ページ遷移に近い変化には使えますが、処理中であることは伝えられず、利用者を元の位置から離します。<output> 要素はstatusの役割を持ちますが、変化の読み上げは明示的な role="status" ほど安定していません。
エラー。 失敗も言葉で伝えます。同じステータス領域に入れるか、すぐに対応が必要ならアラート領域に入れます。
修正の確認
マウスを使わない4つの確認
確認方法: スクリーンリーダー、キーボード
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。データの取得処理や読み込み表示のコンポーネントを変更した際は、再度確認します。
制限
ライブリージョンの動きは、スクリーンリーダーやブラウザーによって異なります。特に、非表示の領域、移動した領域、ダイアログ内の領域では差が出ます。このパターンは、一つのページでの一つの読み込み操作を扱っています。ページ遷移や無限スクロールも含め、利用者が使うスクリーンリーダーで実際の流れを確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。