PR

【コピペ可】最短5行でフォームをリアルタイムバリデーションする

JavaScriptを学ぶ

この記事の要約

  • リアルタイムバリデーションは、フォーム離脱率を20%削減できる可能性がある。
  • 実装にはJavaScript標準API reportValidity() を使う。
  • 最短5行で実装が可能。

こんにちは。フォームを作るとき、ユーザーに分かりやすいようすぐにエラーを表示させて欲しいという要求って多いですよね。いわゆるリアルタイムバリデーションです。実際その方がUX的にいいことが多いです。特に離脱率を下げる効果が期待でき、離脱率が20%軽減した事例もあるようです(参考:https://cocorograph.co/knowledge/what-is-efo/)。

せっかくたくさん入力したのに、送信ボタン押下後にエラーが出るとユーザーはストレスを感じますからね。

ですが、実装方法を調べるとどれも複雑です。大量のJavaScriptコードが必要だったり、あるいはフォーム用のライブラリを使って行うものもありますが、前提となるライブラリ(React、Vueなど)を使っていないといけないなど気軽なものがありません。

そこで今回はWebの標準機能だけを使って簡単にリアルタイムバリデーションを実装する方法を紹介します。

結論(コピペ可)

コード(電話番号の例)

例は電話番号ですがもちろんtype=”email”やtype=”url”など他のものにも使えます。

index.html
<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>
script.js
const tel = document.getElementById("tel");

let skipValidationOnRefocus = false // とある問題を回避するためのフラグ。詳細は後述。

tel.addEventListener("blur", (event) => {
  if (skipValidationOnRefocus) {
    skipValidationOnRefocus = false
    return
  }

  // reportValidity()を使ってバリデーションを行う
  if (!event.target.reportValidity()) {
    skipValidationOnRefocus = true
  }
});
style.css
.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で基本的なパーツを作る方法を学ぶ

【改訂版】【完全案件特化】JavaScriptをどこまで学ぶべきかの学習ロードマップ + 頻出UIの作り方8選 - セカヤサBooks
【最新版!】・HTML/CSSの次は何を勉強すれば良いの?・案件が取れない・未経験だけど転職を考えている・フリーランスで実績を積みたいそんなあなたの悩み、全て解決します。JavaScriptをどこまで深めれば案件をこなせるのか。フリーランス...

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

【脱・初心者】教材だけでは分からない、案件でよく要求される細かいコーディングのテクニック30選 + α - セカヤサBooks
フリーランスのフロントエンドエンジニアとして7年間、サイト制作やWebアプリケーション開発の案件に携わってきた中で「これは伝えたい」と思ったコーディングのテクニックをまとめました!正直言うと、これができているかどうかが「プロかプロでないか」...

よくある質問

Q
ライブラリを使わず、標準 API だけで本当に十分ですか?
A

まず大前提として「どのライブラリ、フレームワークを採用しているか」で選択肢が変わります。たとえば React を使っていない案件でReact Hook FormTanStack FormConformを導入するのは現実的ではありませんし、Vue アプリなら VeeValidateFormKitを採用するほうが開発体験は良いでしょう。基本的に前提となるライブラリに合わせたものを利用するのがおすすめです。

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 {
  // 独自のバリデーション処理
}
Q
デザイナーから「標準のエラー UI はダサい」と言われました。カスタマイズできますか?
A

入力値の検証のみを行うcheckValidity()とエラーメッセージをカスタムできるsetCustomValidity()を使えば可能です。

Q
reportValidity() は古いブラウザ(IE 11 など)で動きますか?
A

IE11やEdge Legacyでは未実装 です。ただしcheckValidity()は利用可能なのでそちらを使って判定→メッセージ表示は独自実装してください。
Can I Use:https://caniuse.com/mdn-api_htmlinputelement_checkvalidity

Q
バックエンドでも同じバリデーションを実装する必要がありますか?
A

必須です。フロント側はあくまでUX 改善・離脱率低減が目的です。改ざん防止、データ整合性はサーバー側バリデーション(ORMSchema、正規表現、型チェック)で担保しないと、何らかの形でリクエストを書き換えられたときに不正なデータが保存されてしまいセキュリティ的にもリスクがあります。

参考

reportValidity()は各要素ごとに解説されているので詳しく知りたい方はチェックしてみてください。

また公式でもフォームのバリデーションに関する学習コンテンツがあります。合わせて参照してください。

この記事を書いた人
小林 秀樹

Web制作フリーランス9年目|ディレクター・フロントエンドエンジニア
「世界一初心者に優しい」→セカヤサ。

コーポレートサイトやLP、メディア立ち上げまで幅広く対応。コーディングを軸に、UX設計・SEOライティング・サイト運用まで一貫対応。

法律系オウンドメディア立ち上げ支援では、特定のキーワードでのSEO1位を実現し問い合わせ獲得に貢献。また大手専門学校サイトの大規模リニューアル(100ページ超)でのディレクションと実装の両面の経験あり。

最近では、AIツールによる自社サービスUI制作や、ChatGPTを活用した記事執筆フローの構築など、AI時代に対応した制作手法の実践にも注力している。

zenn:
https://zenn.dev/hideki_climax

制作実績:
https://tan-band-c66.notion.site/37abe7077b184b338fd8f53f6873ca1d

小林 秀樹をフォローする
JavaScriptを学ぶ
スポンサーリンク
シェアする
小林 秀樹をフォローする

コメント

タイトルとURLをコピーしました