リアルタイム検証
メールアドレスを入力すると形式を即時判定し、✓(緑)/ ✗(赤)とメッセージでフィードバックする。
プレビュー
操作実際に入力して試せます。
仕様書
- 向く場面
- メール・郵便番号など形式を機械的に判定できる欄
- 送信前に体裁の誤りだけ早く気づかせたいフォーム
- ✓/✗ と文言で結果を即時に返したい入力
- 避ける場面
- サーバー問い合わせが要る判定(毎打鍵で叩く)
- 打ち終わる前に赤くしたくない欄(blur まで保留する)
- 構成要素
- ラベル
- 入力欄
- 状態アイコン(✓/✗)
- メッセージ
- 持っている状態
- 通常
- フォーカス
- エラー
- 成功
- キーボード
Tab で欄へ移動し、入力のたびに形式を判定する。特別なキー操作は無い
- 支援技術
label[for] で名前を結び、aria-describedby でメッセージを関連付ける。判定は aria-invalid を切替、メッセージは aria-live=polite。結果は枠色・✓/✗・文言の3つで示し色だけに頼らない。アイコンは aria-hidden
- 動き
枠色・文言色 200ms(状態の切替を目で追える)。reduced-motion で無効
デザインの要点
- 結果は枠色・アイコン・文言の3つで出す。緑と赤の枠だけでは、色の見分けが難しい人に伝わらない
- 空欄は無印に戻す。消したのに ✗ が残ると、何を直せばよいか分からない
- 見本は毎打鍵で判定するが、実運用は初回 blur まで保留し、一度エラーが出た欄だけ入力中も再評価するのが穏当
- 正規表現は形式チェックにすぎない。✓ を「正しいメールアドレス」の保証として扱わない
使いどころ
入力ルールが機械的に判定できる項目(メールアドレスの形式、郵便番号の桁数など)に向く。この実装はメールの正規表現チェックのみを対象にしており、値が入るたびに即座に ✓/✗ を出す。フォーム送信前に体裁の誤りだけでも早期に気づかせたい場面で有効だが、「そのメールアドレスが実在するか」「そのユーザー名が空いているか」のようにサーバーに問い合わせないと判定できない項目には向かない。この実装のようにキー入力ごとに毎回判定する設計は、メールアドレスをまだ途中まで(taro@ex)しか打っていない段階でも✗が出るため、タイピング中は常に赤く表示され続けることになる。ユーザーが打ち終わるまで待つ体験にしたいなら、初回はblur(フォーカスが外れたとき)まで判定を保留し、一度エラーを出した後だけ入力毎に再評価する設計に切り替えるべきである。
仕組み
input要素にinputイベントを1つ張り、validate() が空値・OK・NG の3状態を input.className の付け替え(無印/ok/err)と #iv-msg のテキスト差し替えで表現する。正規表現は /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/ で、@の前後に空白と@を含まない文字列があり、ドット以降に2文字以上あることだけを見る簡易チェックである。アイコンは span.iv-icon の textContent を "✓"/"✗" に差し替え、色は input や msg と同じ ok/err クラスを付け替えて CSS 側で決める。3つの要素が同じクラス名で同期するので、デザイントークンを変更するときは border-color・文言色・アイコン色を CSS だけで一括して直せる。同時に aria-invalid と aria-describedby で、同じ判定結果を支援技術にも渡している。
実装の注意
最大の注意点は、input イベントで毎回 validate() を呼ぶため、1文字打つごとに正規表現評価とDOM書き換えが走ること自体は軽量だが、これを将来「サーバーにユーザー名の重複確認をする」ような非同期処理に置き換えると、キー入力のたびにリクエストが飛んでしまう。デバウンス(300〜500ms程度の遅延)を入れずに非同期化するのは典型的な失敗である。次に、色を JS の style.color で直接書きたくなる場面があるが、CSSのテーマ変更(ダークモード対応など)で見落とされやすいので、この見本のように .ok/.err クラスで枠・文言・アイコンを一元管理する方が保守しやすい。また正規表現は形式チェックに過ぎず、実在しないドメインや意図的なタイポ(gmial.comなど)は素通りするため、「✓が出た=正しいメールアドレス」という過信をユーザーにもコードにも持たせてはいけない。
アクセシビリティ
#iv-msg には aria-live="polite" が付いており、状態が変わるたびのテキスト差し替えがスクリーンリーダーにも届く。「正しい形式です」も「形式で入力してください」も、フィールドを離れて探索しなくても読み上げられる。input 要素側では aria-describedby="iv-msg" でメッセージを関連付け、判定のたびに aria-invalid を "true"/"false" に切り替えている(空欄では属性を外す)。視覚的にはボーダー色とアイコンとメッセージの3つで状態を示しており色だけに頼っていないが、その視覚情報がスクリーンリーダーにも同じ意味で伝わる構造になっているのがポイントである。アイコンの span には aria-hidden="true" が付いており、装飾要素として正しく除外されている。polite なので入力を妨げずに読み上げるが、毎打鍵で判定する設計のままだと読み上げも毎打鍵になるため、実運用では blur まで保留する設計と組み合わせるとよい。
調整して使う
判定タイミングは「毎キー入力」「blur時のみ」「初回はblur・以降は毎キー入力」の3パターンが考えられ、後者2つの方が実運用では好まれる。エラー時のメッセージ表示を aria-live="assertive" にして送信失敗時のみ強く割り込ませ、通常の入力中フィードバックは "polite" に留めるという使い分けも可能。同じ「入力欄+アイコン+メッセージ」の骨格は、パスワード強度メーターや郵便番号形式チェックなど他のフィールドにも転用でき、正規表現とメッセージ文言だけを差し替える関数として切り出すと複数フィールドで再利用しやすくなる。✓表示についても、ユーザーがフィールドを離れる(blur)まで正解を確定表示しない設計にすると、ドメイン部分を打ち終える前に緑になってしまう「早すぎる正解表示」を避けられる。
コード(コピーして使えます)
<div class="iv-wrap">
<label class="iv-label" for="iv-email">メールアドレス</label>
<div class="iv-field">
<input
id="iv-email"
class="iv-input"
type="text"
placeholder="example@mail.com"
autocomplete="off"
aria-describedby="iv-msg"
/>
<span class="iv-icon" aria-hidden="true"></span>
</div>
<p class="iv-msg" id="iv-msg" aria-live="polite"></p>
</div>