こんにちは。フォームを作るとき、ユーザーに分かりやすいようすぐにエラーを表示させて欲しいという要求って多いですよね。いわゆるリアルタイムバリデーションです。実際その方がUX的にいいことが多いです。特に離脱率を下げる効果が期待でき、離脱率が20%軽減した事例もあるようです(参考:https://cocorograph.co/knowledge/what-is-efo/)。
せっかくたくさん入力したのに、送信ボタン押下後にエラーが出るとユーザーはストレスを感じますからね。
ですが、実装方法を調べるとどれも複雑です。大量のJavaScriptコードが必要だったり、あるいはフォーム用のライブラリを使って行うものもありますが、前提となるライブラリ(React、Vueなど)を使っていないといけないなど気軽なものがありません。
そこで今回はWebの標準機能だけを使って簡単にリアルタイムバリデーションを実装する方法を紹介します。
結論(コピペ可)
コード(電話番号の例)
例は電話番号ですがもちろんtype=”email”やtype=”url”など他のものにも使えます。
<form action="" class="Form">
<!-- 電話番号 -->
<label class="Form-Item">
<span class="Form-Item-Label">電話番号</span>
<input
type="tel"
id="tel"
class="Form-Item-Input"
pattern="0[0-9]{9,10}"
title="電話番号を入力してください"
/>
</label>
</form>
const tel = document.getElementById("tel");
let skipValidationOnRefocus = false // とある問題を回避するためのフラグ。詳細は後述。
tel.addEventListener("blur", (event) => {
if (skipValidationOnRefocus) {
skipValidationOnRefocus = false
return
}
// reportValidity()を使ってバリデーションを行う
if (!event.target.reportValidity()) {
skipValidationOnRefocus = true
}
});
.Form-Item {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.Form-Item-Label {
font-weight: 600;
color: #555;
font-size: 14px;
}
.Form-Item-Input {
border-radius: 8px;
border: 2px solid #e1e5e9;
width: 100%;
padding: 16px;
font-size: 16px;
background-color: #fafbfc;
}
.Form-Item-Input:focus {
outline: none;
border-color: #667eea;
background-color: white;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
/* エラーが発生したときは赤くする */
.Form-Item-Input:user-invalid {
border-color: #e74c3c;
background-color: #fdf2f2;
}
/* 正しく入力できたときは緑にする */
.Form-Item-Input:user-valid {
border-color: #27ae60;
background-color: #f8fff8;
}
デモ
実際に色々入力して試してみてください。
inputからフォーカスを外した時、
- 電話番号の形式が正しければチェックが通り、inputが緑になる。
- 正しくなければエラー表示を出し、inputが赤になる。
という動きになり、ユーザーに対して即座にエラーを伝えることができます。
解説
今回のコードでポイントなのは、reportValidity()という標準メソッドです。Constraint Validation APIの中で実装されています。
inputに対する入力が正しければtrueを返し、正しくなければfalseを返した上で画面上にエラーメッセージ(下画像)を表示します。
const tel = document.getElementById("tel");
const isValid = tel.reportValidity()
// ルールに従っていればtrueが返り、違反していればfalseが返る + 画面表示がされます。

ここで「入力が正しい」とはタグの属性として指定できるバリデーションルールに対して正しいことを指しています。(参考:https://developer.mozilla.org/ja/docs/Web/HTML/Guides/Constraint_validation)
例えばpattern属性で正規表現が指定されている場合はそれを満たさなければfalseに、required属性があれば入力が空の時にfalseになります(下例)。
<input
type="tel"
id="tel"
pattern="0[0-9]{9,10}"
required
/>
指定されている全ての条件を検証するので、1つでも満たさなければfalseになります。これを使えば入力値判定 + 画面表示を一発で行ってくれます。とても簡単で嬉しいですね。
このreportValidity()は、条件が指定できるタイプのinput、select、textarea、button要素、またはform要素に利用することができます。
form要素に対して呼び出した場合は子要素全てを一気にチェックし、エラーがある要素にメッセージを表示してくれます。ちなみにformの送信ボタンの標準動作もそうなっているので特に追加の実装をする必要はありません。ありがたいですね。
さて、リアルタイムバリデーションについて考えましょう。ユーザーが一旦入力を終え、inputからフォーカスを外した時(=blur時)に検証するのが良さそうです。よってblurイベント時にreportValidity()を呼び出しましょう。
const tel = document.getElementById("tel");
tel.addEventListner("blur", (event)=>{
event.target.reportValidity()
})
これでOKと思いきや、「入力が間違っている限りフォーカスが外れなくなる」という問題が発生してしまいます。次のデモで確かめてみてください。「000」などを入力してフォーカスを外してみてください。
※requierd属性は指定していないので入力を空にするとループを抜け出せます。
フォーカスを外す = blurイベントの発火 → 入力が間違っているとreportValidity()が画面表示を行う → その際、仕様で自動的にinputにフォーカスを当て直してしまう → フォーカスを外す(以下ループ)
という挙動になってしまいます。これはユーザビリティが良くないですね。そこで変数を1つ追加し「エラーが発生した場合、次のblur時は何もしない」ようにしましょう。
const tel = document.getElementById("tel");
let skipValidationOnRefocus = false
// 1. reportValidity()による再フォーカス時を判定するフラグを追加。
tel.addEventListener("blur", (event) => {
// 3. 再フォーカス発生直後はblurしても何もしないことでループを回避する。
if (skipValidationOnRefocus) {
skipValidationOnRefocus = false
return
}
if (!event.target.reportValidity()) {
skipValidationOnRefocus = true
// 2. エラーが発生したら再フォーカスが発生するので、フラグをONにする。
}
});
こうすることで期待通りの挙動になります。
これで動作は完成ですね。
見た目についてですが、:user-valid / :user-invalid擬似クラスを使うことでそれぞれバリデーション成功 / 失敗時の見た目を指定することができます。
/* エラーが発生したときは赤くする */
.Form-Item-Input:user-invalid {
border-color: #e74c3c;
background-color: #fdf2f2;
}
/* 正しく入力できたときは緑にする */
.Form-Item-Input:user-valid {
border-color: #27ae60;
background-color: #f8fff8;
}
昔から:valid / :invalid擬似クラスは存在していましたが、これだとユーザーがまだ何もしていない時点で入力値を判定してしまい、ページにアクセスした瞬間に緑もしくは赤になってしまいます。:user-valid / :user-invalidはその点を解決しているのでおすすめです。
Can I Useを見ても、どちらもモダンなブラウザであればほぼ問題なく利用できそうです。
▼ Can I Use
https://caniuse.com/mdn-css_selectors_user-valid
https://caniuse.com/mdn-css_selectors_user-invalid
▼ 参考(MDN)
http://developer.mozilla.org/ja/docs/Web/CSS/:user-valid
https://developer.mozilla.org/ja/docs/Web/CSS/:user-invalid
まとめ:標準機能だけでも十分なバリデーションは可能
というわけで、簡単にリアルタイムバリデーションを実装する方法を紹介しました。細かいテクニックが、ビジネスには直接的にインパクトを与えることもあります。ぜひ意識してみてください。
ただし、言うでもなくフロントエンドのバリデーションはあくまでUX向上が大きな役割であり必ずバックエンドでもバリデーションを行なってくださいね。
▼ JavaScriptで基本的なパーツを作る方法を学ぶ

▼ さらにUXを向上させる細かなテクニックを身につける

よくある質問
- Qライブラリを使わず、標準 API だけで本当に十分ですか?
- A
まず大前提として「どのライブラリ、フレームワークを採用しているか」で選択肢が変わります。たとえば React を使っていない案件でReact Hook FormやTanStack Form、Conformを導入するのは現実的ではありませんし、Vue アプリなら VeeValidateやFormKitを採用するほうが開発体験は良いでしょう。基本的に前提となるライブラリに合わせたものを利用するのがおすすめです。
- Qスマホ(iOS / Android)でも同じコードで動きますか?
- A
モダン端末で動く主要ブラウザ、Safari、Chrome、Samsung Internet などはいずれもreportValidity()をフルサポートしています。参考:https://caniuse.com/mdn-api_htmlformelement_reportvalidity
ただし例外もあります。
- 専用業務端末向けブラウザ(Honeywell Enterprise Browser など)は HTML5 の
required
属性や Constraint Validation APIを実装していません。sps-support.honeywell.com - キャリアやMDMポリシー(企業が従業員のモバイルデバイスを管理・保護するためのルール)で JavaScriptを制限した環境では動作しないことがあります。
対策としてはメソッドの存在チェックをし、なければ独自の処理をする形にしましょう。
if (typeof input.reportValidity === 'function') { input.reportValidity(); } else { // 独自のバリデーション処理 }
- 専用業務端末向けブラウザ(Honeywell Enterprise Browser など)は HTML5 の
- Qデザイナーから「標準のエラー UI はダサい」と言われました。カスタマイズできますか?
- A
入力値の検証のみを行うcheckValidity()とエラーメッセージをカスタムできるsetCustomValidity()を使えば可能です。
- QreportValidity() は古いブラウザ(IE 11 など)で動きますか?
- A
IE11やEdge Legacyでは未実装 です。ただしcheckValidity()は利用可能なのでそちらを使って判定→メッセージ表示は独自実装してください。
Can I Use:https://caniuse.com/mdn-api_htmlinputelement_checkvalidity
- Qバックエンドでも同じバリデーションを実装する必要がありますか?
- A
必須です。フロント側はあくまでUX 改善・離脱率低減が目的です。改ざん防止、データ整合性はサーバー側バリデーション(ORMSchema、正規表現、型チェック)で担保しないと、何らかの形でリクエストを書き換えられたときに不正なデータが保存されてしまいセキュリティ的にもリスクがあります。
参考
reportValidity()は各要素ごとに解説されているので詳しく知りたい方はチェックしてみてください。
- https://developer.mozilla.org/ja/docs/Web/API/HTMLInputElement/reportValidity
- https://developer.mozilla.org/ja/docs/Web/API/HTMLFormElement/reportValidity
- https://developer.mozilla.org/ja/docs/Web/API/HTMLButtonElement/reportValidity
- http://developer.mozilla.org/ja/docs/Web/API/HTMLSelectElement/reportValidity
- https://developer.mozilla.org/ja/docs/Web/API/HTMLTextAreaElement/reportValidity
また公式でもフォームのバリデーションに関する学習コンテンツがあります。合わせて参照してください。
コメント