トピックを検索
読み上げられないお礼の文に差し替わるフォーム
送信に成功すると、メールマガジンの登録フォームが、読み上げられないお礼の段落に差し替わります。フォーカスのあった「登録する」ボタンも一緒に消えるため、フォーカスはページそのものに落ちます。
signup.replaceWith(thanks); 問題
スクリーンリーダーの利用者は「登録する」を押しても何も聞こえず、操作していたボタンも消えるため、登録できたのかも、ページのどこにいるのかも分かりません。
書店のサイトのフッターに、メールマガジンの登録欄があります。「メールマガジンに登録」の見出し、「メールアドレス」の欄、「登録する」ボタンです。送信は裏側で処理され、失敗したときは下のステータス行で知らせる作りになっています。成功したときの見せ方は違います。欄とボタンが消え、その場所に「ありがとうございます。確認メールをお送りしました。」と表示されます。すっきりしていて、手続きが終わったことも伝わります。ところがスクリーンリーダーの利用者は、「登録する」を押しても何も聞こえません。操作していたボタンはもうなく、フォーカスの行き場がありません。読み進めようとすると、ページの別の場所に出てしまうこともあり、登録できたかどうかは分からないままです。
「ありがとうございます。確認メールをお送りしました。」は、フォーカスを受けない限りステータスメッセージです。達成基準4.1.3は、ステータスメッセージを役割やプロパティで支援技術に伝え、フォーカスを動かさなくても読み上げられるようにすることを求めています。フォーカスを受ける内容は、この基準の対象外です。フォーカスを受けることはコンテキストの変化にあたり、内容もフォーカスが届いたときに読まれるからです。つまり、フォーカスを移すのも基準を満たす方法の一つです。この例のお礼の文は、どちらの方法もとっていません。役割のない新しい p で、ライブリージョンにも入っておらず、フォーカスも移されていません。これが失敗例F103です。しかも、フォーカスのあった「登録する」ボタンはフォームと一緒に削除されます。するとHTML標準のfocus fixupの手順が働き、フォーカスはページのbodyへ移ります。スクリーンリーダーには、そこで新しく読むものがありません。
お礼の文はフォームのあった場所に表示されるため、フッターを見ている人には見えます。見落とすのは、音声や点字で伝わる内容に頼っている人です。操作していたボタンまで、フォームと一緒に失います。
10グループ中1グループに影響
- 全盲 (影響あり)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響なし)
- 届く範囲・力 (影響なし)
- 認知 (影響なし)
- 光過敏 (影響なし)
- 視覚を使わない スクリーンリーダーの利用者は「登録する」を押しても何も聞こえず、登録できたか分かりません。操作していたボタンもフォームと一緒に消えるため、ページ上の自分の位置を探し直すことになります。
スキャンは、ラベルのある欄、名前のあるボタン、ステータス行を見て、フォームを合格にします。本番のサイトで送信すれば実際に誰かが登録されてしまうため、フォームは送信しません。そのため、フォームがお礼の文に差し替わるところを見ることはありません。EaseWebの監査は、フォームがあり、ライブリージョンがまったくないページを手がかりとして報告しますが、このページには失敗用のステータス行があるため、この手がかりは出ません。フォームが差し替わることを、マークアップから判断できるチェックもありません。お礼の文を読み上げさせるよう頼まれたAIアシスタントは、新しい段落を作るときに role="status" を付けることがあります。これでは領域が文言と一緒に現れ、読まれないことがあります。フォームをライブリージョンで包む例もありますが、お礼の文は読まれても、フォーカスはページのbodyに残ります。スクリーンリーダーを動かしたまま実際に送信して、初めて何が読まれ、フォーカスがどこへ行くか分かります。
試してみる
「登録する」を押して読み上げを聞き、続けてTabを押してください。
ページの下に、フォーカスの行き先とスクリーンリーダーの読み上げが表示されます。お礼の文は聞こえますか?次のTabはどこから始まりますか?
問題あり 修正できますか?
スクリーンリーダーの読み上げ:
(「登録する」を押してください)
修正方法
結果がフォームの場所に入るならそこへフォーカスを移し、そうでなければフォームを残して結果をステータス行に書き込みます。
- 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" role="status"></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>失敗は伝わります。ステータス行に書き込まれ、フォームもフォーカスのあるボタンも残るからです。抜けているのは成功のときです。新しい段落には役割がなく、フォーカスも移されないため、読まれません。さらに replaceWith() がフォーカスのあった「登録する」ボタンを削除するため、フォーカスはページのbodyに落ちます。
デザインはそのままです。お礼の文には tabindex="-1" を付けます。Tabの順序には加えずに、スクリプトからフォーカスを移せるようにするためです。ページに入れた直後にフォーカスを移すので、スクリーンリーダーはフォーカスが届いたときに読み上げ、次のTabもそこから、フォームの後ろへ続きます。位置を見失う人はいません。失敗のときは変わりません。フォームが残り、フォーカスは「登録する」に残り、何が起きたかはステータス行が伝えます。
どちらもお礼の文を読み上げさせ、消えたボタンにフォーカスを置き去りにしません。デザイン上どうしてもフォームを差し替えるなら、お礼の文へフォーカスを移します。スクリプト2行で済み、見た目は描かれたとおりです。デザインを変えられるなら、フォームを残します。フォーカスは動かず、一つのステータス行で成功も失敗も伝えられ、アドレスも画面に残って確かめられます。どちらが良いかはサイトによります。フォームを消す必要があるかどうかで決まります。
メッセージ全体にフォーカスを移します。 アイコン、「完了」とだけ書いた見出し、ページの先頭ではなく、結果を文で伝えている要素にフォーカスを移します。お礼の文は一度で読み切れる短さにします。見出しが必要なら、「登録が完了しました」のように見出しで結果を伝え、その見出しにフォーカスを移します。
新しいマークアップの描画後に。 コンポーネントのフレームワークでは、成功の分岐でフォームを {#if} の中で入れ替えたり、フッター全体を描き直したりすることがよくあります。これも replaceWith() と同じく、フォーカスのあるボタンを削除します。新しい内容がページに入ってから(Svelteの tick()、Reactのエフェクト、Vueの nextTick() のあとで)フォーカスを移し、あとの描き直しでフォーカスした要素が置き換わらないことも確かめます。
フォーカスの枠。 キーボードで操作したあとは、フォーカスを受けた段落の周りに、たいていのブラウザーがフォーカスの枠を描きます。消さないでください。画面を見ながらキーボードで操作する人には、この枠がいまの位置の目印になります。マウスでクリックしたあとは、通常は表示されません。
失敗のときはそのまま。 送信に失敗したらフォームを差し替えてはいけません。もう一度送るためにフォームが必要だからです。どちらの修正でも、フォーカスは「登録する」に残し、失敗はステータス行に書き込みます。この行と、メッセージの前に行を空にする理由は、読み上げられないフォームの送信結果で説明しています。
埋め込みのフォーム。 メール配信サービスやマーケティングサービスのフォームには、フォームを独自のお礼の文に差し替えるスクリプトが付いてくることがあります。自分のページに埋め込んだ状態で確認してください。スクリプトを変えられない場合は、お礼のページへ移動させる設定がないか確かめます。新しいページなら、スクリーンリーダーが新しいページとして伝えます。
修正にならないもの。 新しい段落を作るときに role="status" や aria-live を付けると、領域が文言と一緒に現れることになり、読まれないことがよくあります。この失敗は画面に出ても読み上げられないステータスメッセージで扱っています。フォームをライブリージョンで包んでその中で差し替えると、お礼の文は読まれますが、フォーカスはページのbodyに落ちたままです。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
デザインの案
お礼の表示がフォームの場所に入るなら、フォーカスも一緒に移せます。
フォームをお礼の表示に差し替える、3通りの描き方です。それぞれTabで「登録する」へ移動してEnterを押してください。2つはフォーカスの枠がお礼の表示に移り、1つはどこにも残りません。
デザインファイルで
- お礼の表示が現れたら、キーボードのフォーカスがどこに移るかに印を付け、フォーカスの枠も描く。
- お礼の文は、何が起きたかと次に何をすればよいかを、一文で書く。
- 送信に失敗したときも描く。フォームはそのまま残し、その下にメッセージを出す。
- お礼の表示の色の横にコントラスト比を書く。文字は最低4.5:1。
合格
- 次の手順付きのカード フォームが白いカードに替わります。カードには青緑のチェックマーク、「メールをご確認ください」、アドレスが並び、「別のアドレスを使う」ボタンでフォームに戻れます。カードは現れると同時にフォーカスを受けるため、最初の行が読まれ、次のTabでボタンに届きます。青緑と白地のコントラスト比は9.08:1です。
- 封筒の絵と中央ぞろえの見出し 淡い青緑の円に入った封筒の絵の下に、大きな文字の「登録が完了しました」と、次の手順を伝える一行が、フォームのあった場所の中央に並びます。見出しがフォーカスを受けるため、最初に読まれ、続く一行も読めます。白地の文字は14.66:1です。
不合格
- フォームの場所に浮かび上がる「登録しました!」WCAG 4.1.3 フォームが消え、チェックマークと「登録しました!」が0.3秒ほどでその場所に浮かび上がります。動きは変化を示して止まりますが、何もフォーカスを受けません。Enterのあと、フォーカスの枠はどこにも表示されず、スクリーンリーダーにも何も聞こえません。新しい行にフォーカスを移せば、動きはそのまま残せます。この見本では、4秒後にフォームが戻ります。
修正の確認
マウスを使わない4つの確認
確認方法: スクリーンリーダー、キーボード
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。フォームのコンポーネント、スクリプト、埋め込みを変更した際は、再度確認します。
制限
フォーカスを受けた段落で何を読むか、削除された要素のあとどこから続けるかは、スクリーンリーダーやブラウザーによって異なります。このパターンは、成功するとその場で差し替わる短いフォーム一つを扱っています。ページに残って結果を伝えるフォームは読み上げられないフォームの送信結果で、新しいページや画面へ移るフォームは読み上げられないSPAの画面遷移で扱っています。埋め込みのフォームも含め、実際のフォームを、利用者が使うスクリーンリーダーで確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。