トピックを検索
読み上げられないフォームの送信結果
メールマガジンの登録フォームをページ遷移なしで送信し、結果の「ありがとうございます。確認メールをお送りしました。」をただの段落に書き込む作りです。画面には結果が出ても、スクリーンリーダーでは読み上げられません。
message.textContent = 'ありがとうございます。確認メールをお送りしました。'; 問題
スクリーンリーダーの利用者は「登録する」を押しても何も聞こえず、登録できたのか分からないため、もう一度送るか、あきらめます。
書店のサイトのフッターに、小さなメールマガジンの登録欄があります。「メールマガジンに登録」の見出し、「メールアドレス」の欄、「登録する」ボタンです。送信は裏側で処理され、ページは再読み込みされません。1秒ほどで、その下に「ありがとうございます。確認メールをお送りしました。」と表示されます。書店のメール配信サービスが止まっていれば、同じ行に「登録できませんでした。もう一度お試しください。」と出ます。画面が見える人は、その一文を読んで次へ進みます。スクリーンリーダーの利用者は、「登録する」を押しても、成功でも失敗でも何も聞こえません。フォーカスはボタンに残り、ボタンの名前も「登録する」のままで、下の行は読まれません。もう一度押して確認メールが2通届くか、登録できたか分からないままページを離れます。
「ありがとうございます。確認メールをお送りしました。」はステータスメッセージです。利用者の操作の結果を伝え、フォーカスは移しません。達成基準4.1.3の例として、W3Cは「フォームを送信すると、送信できたことを伝える文がフォームに追加される」場面を挙げています。この基準が求めているのは、そうしたメッセージを役割やプロパティで支援技術に伝え、フォーカスを動かさなくても読み上げられるようにすることです。この例では、結果を role="status" も aria-live もないただの p に書き込んでいるため、ブラウザーは変化があったことをスクリーンリーダーに知らせません。これが失敗例F103です。失敗のメッセージもステータスメッセージで、同じように基準を満たしていません。
メッセージはフォームのすぐ下にあるため、フォームを見ている人には見えます。見落とすのは、音声や点字で伝わる内容に頼っている人だけです。フォームがメール配信サービスの埋め込みなら、そのフォームを載せたどのページでも、同じように何も読み上げられません。
10グループ中1グループに影響
- 全盲 (影響あり)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響なし)
- 届く範囲・力 (影響なし)
- 認知 (影響なし)
- 光過敏 (影響なし)
- 視覚を使わない スクリーンリーダーの利用者は「登録する」を押しても何も聞こえず、登録できたか分かりません。
スキャンはページを読み込み、ラベルのある欄と名前のあるボタンを持つフォームを見て合格にします。フォームは送信しません。本番のサイトで送れば、実際に誰かが登録されてしまうからです。そのため結果が現れるところを見ることはなく、空の段落からは、そこに結果が入ることも分かりません。EaseWebの監査は、フォームがあり、ページのどこにもライブリージョンがない場合を、手で確認すべき手がかりとして報告します。クッキーバーなど、ほかの場所にライブリージョンがあるページではこの手がかりが出ないため、やはり人が送信して確かめる必要があります。修正を頼まれたAIアシスタントは、段落に文言を入れるのと同時に aria-live を付けることがあります。これでは領域と文言が一緒に現れます。スクリーンリーダーを動かしたまま、成功と失敗の両方で実際に送信して、初めて何が読まれるか分かります。
試してみる
「登録する」を押して、読み上げを聞いてください。
フォームの下に、スクリーンリーダーの読み上げが表示されます。結果は聞こえますか?「送信を失敗させる」をオンにして、もう一度、2回押してください。
問題あり 修正できますか?
Fernhill Books
新刊は毎週木曜日に入荷します。営業時間は毎日9時から18時です。
スクリーンリーダーの読み上げ:
(「登録する」を押してください)
修正方法
結果は、送信前からページにあるステータス領域に書き込み、フォーカスは動かしません。
- 1結果は、送信前からページにあるステータス領域に書き込み、中身だけを変える。
- 2送信できた結果も、できなかった結果も、言葉で伝える。
- 3フォーカスはボタンに残す。メッセージへ移したり、送信中にボタンを無効にしたりしない。
<section aria-labelledby="signup-title"> <h2 id="signup-title">メールマガジンに登録</h2> <form id="signup"> <label for="signup-email">メールアドレス</label> <input id="signup-email" name="email" type="email" autocomplete="email" required> <button>登録する</button> </form> <p id="signup-message"></p></section><section aria-labelledby="signup-title"> <h2 id="signup-title">メールマガジンに登録</h2> <form id="signup"> <label for="signup-email">メールアドレス</label> <input id="signup-email" name="email" type="email" autocomplete="email" required> <button>登録する</button> </form> <p id="signup-message" role="status"></p></section>この方法を選ぶ理由:メッセージは対象のそばに出す
段落は最初からページにありますが、ライブリージョンではないため、書き込んでも変わるのは見た目だけで、読み上げは何も変わりません。フォーカスは変化のない「登録する」に残るので、そこにもスクリーンリーダーが新しく読むものはありません。
もとからある段落に role="status" を付けるので、文言が変わるときには、ブラウザーがすでにこの領域を監視しています。スクリーンリーダーは読み上げ中の内容を終えてから「ありがとうございます。確認メールをお送りしました。」と読みます。フォーカスは「登録する」に残り、利用者はそのまま続けられます。失敗も同じように読まれます。announce() は行を空にし、0.1秒後に文言を書き込むため、同じ文言の2回目の失敗も変化になります。この間がないと、オフラインのようにすぐ失敗するリクエストでは、空にするのと書き込むのが同時に起き、2回目が読まれないことがあります。メッセージはフォームの下に残り、ボタンを押したばかりの人がもう見ている場所にあります。
領域を先に用意します。 コンポーネントのフレームワークでは、ステータス行を空の状態でフォームと一緒に描画します。結果と一緒に現れる {#if} の中には置きません。メッセージと一緒に現れる領域は、読み上げられないことがよくあります。この失敗は画面に出ても読み上げられないステータスメッセージで詳しく扱っています。
読み上げの強さ。 role="status" はスクリーンリーダーの読み上げが終わるのを待ちます。ここではそれで十分です。利用者は「登録する」を押したばかりで、答えを待っているからです。role="alert" は、すぐに対応が必要なメッセージのためにとっておきます。理由は定型の通知でもVoiceOverの読み上げを遮るアラートで説明しています。
送信中。 ボタンは押せるままにして、送信中に押されても無視します。フォーカスのあるボタンに disabled を付けると、ブラウザーがフォーカスをページに落とすことがあり、利用者は自分の位置を見失います。サービスの応答が遅くなりうる場合は、同じステータス行に「登録しています…」と書き込み、結果で置き換えます。読み上げられない読み込み中の表示が読み込み中のメッセージに使っているのと同じやり方です。応答が来たら取り消す短いタイマーから書き込めば、すぐに応答が来たときに2回伝えずに済みます。
欄のエラー。 ブラウザーやサーバーが受け付けないアドレスは、送信の結果ではなく欄のエラーです。直し方がわからないフォームエラーのように、欄のそばに、欄と結び付けて出します。項目の多いフォームで複数のエラーが出る場合は、どの欄が誤りか示さないエラー概要のように、すべての欄を挙げる概要が必要です。そのときステータス行では、送信できなかったことだけを伝えます。
お礼のページ。 再読み込みしたり新しいページへ移ったりするフォームは、ステータスメッセージではありません。新しいページのタイトルと見出しが、何が起きたかを伝えます。シングルページアプリでお礼の画面へ切り替わる場合は、新しい画面として伝える必要があります。読み上げられないSPAの画面遷移で扱っています。
埋め込みのフォーム。 メール配信サービスやマーケティングサービスが提供するフォームには、独自のスクリプトと成功メッセージが付いてきます。中には、フォーカスを移さずにフォームを差し替えるものもあります。自分のページに埋め込んだ状態で確認してください。スクリプトを変えられない場合は、お礼のページへ移動させる設定がサービスにないか確かめます。
修正にならないもの。 フォームがページに残るのにメッセージへフォーカスを移せば、読み上げられはしますが、直したいかもしれないフォームから利用者を引き離します。ステータス行なら、利用者を動かさずに済みます。フォーカスを移すのが修正になるのは、フォームが消える場合だけです。お礼の文に差し替わるフォームのガイドで扱っています。メッセージを書き込むのと同時に段落へ aria-live を付けるのは、ステータスメッセージのガイドのトーストと同じ失敗です。ライブリージョンと文言が一緒に現れます。ボタンの title や aria-label を変えても、確実には読まれず、失敗も伝わりません。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
デザインの案
お礼の表示は、親しみやすさと読み上げを両立できます。
登録できたことを示す、3通りの描き方です。それぞれ「登録する」を押してみてください。2つは結果を言葉にしてスクリーンリーダーにも伝え、1つは絵でしか示しません。
デザインファイルで
- 送信できたときと、できなかったときの2つのメッセージを、フォームの横に全文で書く。
- メッセージはボタンの下に置く。押した人が次に見る場所だから。
- 送信中にボタンに何を表示するかを、ラベルと同じ幅で、ボタンに見えるまま描く。
- メッセージの色の横にコントラスト比を書く。文字は最低4.5:1。
合格
- アドレスを添えた色付きのお知らせ 欄の下に、封筒のアイコンが付いた淡い青緑のお知らせが現れます。太字の「あと少しです。」に続き、店の文字色で「[email protected] にリンクをお送りしました。」(12.40:1)。アドレスを見せるので、打ち間違いにすぐ気づけます。文言は、押す前からあるステータス行に書き込みます。
基本原則:メッセージは対象のそばに出す
- 待ち時間を示すボタン サービスが応答するまで、「登録する」は幅と青緑(9.08:1)を保ったまま、小さく回る輪と「送信中…」を表示します。応答が来れば輪は止まります。結果はそのあと、フォームの下に店の文字色で表示し、ステータス行に書き込みます。フォーカスのあるボタンのラベルの変化は、環境によっては読まれないためです。
不合格
- 紙吹雪と、チェックマークに変わるボタンWCAG 4.1.3 「登録する」が丸い青緑のチェックマークに縮み、紙吹雪が舞って1秒足らずで消えます。ごほうびのようで楽しいものの、結果が一度も言葉になりません。スクリーンリーダーには何も聞こえず、動きを減らす設定ではチェックマークだけが残ります。下のステータス行に文を書き込めば、見た目はそのまま残せます。
修正の確認
マウスを使わない4つの確認
確認方法: スクリーンリーダー、キーボード
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。フォームのコンポーネント、スクリプト、埋め込みを変更した際は、再度確認します。
制限
ライブリージョンの動きは、スクリーンリーダーやブラウザーによって異なります。このパターンは、その場で送信し、送信後もページに残る短いフォーム一つを扱っています。フォームをお礼の文に差し替えるデザインでは、フォーカスのあったボタンも消えるため、ステータス行だけでは足りず、お礼の文へフォーカスを移す必要があります。この場合は読み上げられないお礼の文に差し替わるフォームで扱っています。カートへの追加など、フォームではない操作についてはバッジしか変わらない「カートに入れる」ボタンで、その場で更新される検索結果については件数が読み上げられない検索・絞り込み結果で扱っています。埋め込みのフォームも含め、実際のフォームを、成功と失敗の両方で、利用者が使うスクリーンリーダーで確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。