バリデーションとは何か
バリデーションは「入力が条件を満たしているか」をチェックして、誤入力や欠落を防ぐ仕組みです。HTML とブラウザは、required・type・min/max・pattern などの属性だけで多くの検証を自動でやってくれます。ここが重要です:まずは“ブラウザ組み込みの検証”を最大限に活用し、その上で不足分のみ JavaScript で補うと、コードが短くて堅牢になります。
組み込みバリデーションの基本(required / type / min・max・step)
必須入力(required)
<input id="email" type="email" required placeholder="メールアドレス">
<script>
// 送信時にブラウザが未入力を検出し、エラーバブルを表示
</script>
HTMLここが重要です:required は「空を禁止」。submit 時に自動検出され、ブラウザがわかりやすいエラーを出します。動作はローカルで完結するので、JS がなくても安心です。
入力形式(type)
<input id="mail" type="email" required>
<input id="url" type="url">
<input id="num" type="number" min="0" step="1">
HTMLここが重要です:type を正しく選ぶだけで、基本的な形式チェック(メール・URL・数値)が付きます。スマホではキーボードも適切に切り替わるため、入力体験も向上します。
範囲と刻み(min / max / step)
<input id="age" type="number" min="0" max="120" step="1" required>
HTMLここが重要です:数値に“下限・上限・刻み”を設けられます。組み込み検証は「送信時に不正を止める」役割で、ロジック側でも Number(…) で数値化してから再検証するとさらに安全です。
パターン検証(pattern と title の活用)
正規表現で形式を限定する(半角英数字8〜16文字)
<input id="user" pattern="^[A-Za-z0-9]{8,16}$" title="半角英数字8〜16文字">
HTMLここが重要です:pattern は入力全体に対する正規表現。ユーザーに“何が正しいか”を伝えるために title を必ずセットすると、エラー表示が分かりやすくなります。
日本語用の例(郵便番号 123-4567 または 1234567)
<input id="zip" inputmode="numeric" pattern="^\d{3}-?\d{4}$" title="例: 123-4567 または 1234567">
HTMLここが重要です:記号の任意を「-?」で表現。inputmode=”numeric” を付けるとモバイルで数字キーボードが出て、打ちやすくなります。
組み込み検証の使い方(checkValidity / reportValidity / :invalid)
送信前に自前でチェック
<form id="f">
<input name="email" type="email" required>
<button>送信</button>
</form>
<script>
f.addEventListener("submit", (e) => {
if (!f.checkValidity()) {
e.preventDefault(); // 送信を止める
const firstInvalid = f.querySelector(":invalid");
firstInvalid?.focus(); // 最初のエラーへフォーカス誘導
}
});
</script>
HTMLここが重要です:checkValidity() は“すべての組み込み属性にもとづく検証”を一度に実行。失敗時は preventDefault で止め、:invalid 疑似クラスで最初の不正項目へフォーカスすると、再入力がスムーズです。
検証メッセージを画面に出す(reportValidity)
<script>
// 任意のタイミングで “ブラウザ標準のバブル” を出す
f.reportValidity(); // NG があれば、その場でエラーバブル表示
</script>
HTMLここが重要です:reportValidity() はユーザーに見えるエラーを即表示。独自 UI を作らなくても、最短で親切なフィードバックが用意できます。
入力途中の見た目(CSS)
input:invalid { border-color: #d00; }
input:valid { border-color: #0a0; }
CSSここが重要です::invalid/:valid を使うと、JSなしで視覚フィードバックが可能。過度な“赤警告”は入力途中のストレスになるので、確定後(blur)に強い色を当てるなどの配慮が有効です。
JavaScriptで不足分を補う(カスタム検証とセット)
独自ルールを足す(メール重複チェックなど)
<input id="mail" type="email" required>
<script>
async function checkDuplicate(email) {
// サーバーへ問い合わせる想定
return false; // 重複なし(例)
}
mail.addEventListener("blur", async () => {
const dup = await checkDuplicate(mail.value.trim());
if (dup) {
mail.setCustomValidity("このメールアドレスは既に利用されています");
} else {
mail.setCustomValidity(""); // エラー解除
}
mail.reportValidity();
});
</script>
HTMLここが重要です:setCustomValidity() は“組み込み検証に自前のメッセージを統合”する仕組み。空文字をセットすると解除されます。非同期検証は blur など確定寄りのタイミングにすると体験が安定します。
2項目の整合チェック(パスワード再入力)
<input id="pw" type="password" required>
<input id="pw2" type="password" required>
<script>
function syncConfirm() {
if (pw2.value && pw2.value !== pw.value) {
pw2.setCustomValidity("パスワードが一致しません");
} else {
pw2.setCustomValidity("");
}
}
pw.addEventListener("input", syncConfirm);
pw2.addEventListener("input", syncConfirm);
</script>
HTMLここが重要です:相互依存の検証は“どちらかが変わったら両方再評価”。一致したら必ずエラー解除(空文字)を忘れずに。
バリデーションの設計指針(タイミング・メッセージ・アクセシビリティ)
いつ検証するか(入力中と確定後のメリハリ)
- 入力中は軽いヒントや弱い表示(placeholder やヘルプ文)。
- 確定後(blur)と送信前(submit)で強めのエラー表示。
ここが重要です:日本語入力(IME)中に厳しい検証や整形を走らせると体験が悪化します。compositionstart/compositionend を使って、確定後に本検証するのが安全です。
エラーメッセージは具体的に
- 「形式が正しくありません」より「半角英数字8〜16文字です」のほうが修正しやすい。
- pattern には必ず title を付け、検証理由を明示。
ここが重要です:ユーザーが“どう直せば良いか”をすぐ理解できるメッセージが、離脱や誤修正を減らします。
アクセシビリティの配慮
- エラー時は最初の不正項目に focus()。
- 可能なら aria-live でエラーテキストを読み上げ対応に。
- 色だけに頼らず、テキストでも理由を示す。
ここが重要です:操作性と理解のしやすさを合わせて設計すると、誰にとっても使いやすいフォームになります。
実践例(検索フォーム、会員登録、住所入力)
検索フォーム(必須+最小文字数)
<form id="search">
<input id="q" name="q" required minlength="2" placeholder="キーワード">
<button>検索</button>
</form>
<script>
search.addEventListener("submit", (e) => {
if (!search.checkValidity()) {
e.preventDefault();
q.reportValidity();
return;
}
// 検索処理へ
});
</script>
HTMLここが重要です:minlength は“テキストの下限”。短すぎる誤検索を防ぎ、ノイズを減らします。
会員登録(メール・パスワードの組み合わせ)
<form id="signup">
<input id="mail" name="email" type="email" required>
<input id="pw" name="password" type="password" required pattern="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$" title="英字と数字を含む8文字以上">
<input id="pw2" name="password2" type="password" required>
<button>登録</button>
</form>
<script>
function match() {
pw2.setCustomValidity(pw2.value && pw2.value !== pw.value ? "パスワードが一致しません" : "");
}
pw.addEventListener("input", match);
pw2.addEventListener("input", match);
signup.addEventListener("submit", (e) => {
if (!signup.checkValidity()) {
e.preventDefault();
signup.reportValidity();
}
});
</script>
HTMLここが重要です:pattern で「強度の最低限」を担保し、再入力一致は custom validity で補う。役割分担が明確だと保守しやすいです。
住所入力(郵便番号での補助)
<form id="addr">
<input id="zip" name="zip" inputmode="numeric" pattern="^\d{3}-?\d{4}$" required title="例: 123-4567">
<input id="city" name="city" required>
<button>送信</button>
</form>
<script>
addr.addEventListener("submit", (e) => {
if (!addr.checkValidity()) {
e.preventDefault();
addr.reportValidity();
return;
}
// ここで zip から住所補完APIを呼ぶなどの拡張が可能
});
</script>
HTMLここが重要です:まずは形式を正しく、次に補助をかける。この順序なら「間違った値でAPIを叩く」無駄が減ります。
よくある落とし穴と回避策
未入力と“空白だけ”を同一視してしまい、required を通過してしまうことがあります。送信前に trim() して空ならエラーにするルールを加えると安全です。pattern が「部分一致」になっていて、意図しない通過が起きるケースでは、^ と $ で“全体一致”にするのを忘れないでください。JavaScriptだけで全部検証しようとすると、モバイルキーボードやブラウザUIの恩恵を失いがちです。まず属性でできることを最大化し、不足だけ custom validity に寄せましょう。非同期検証(重複チェックなど)は入力途中に連発すると負荷・体験ともに悪化します。blur や submit 前にまとめて実施し、結果に応じて setCustomValidity → reportValidity の順で表示するのが安定します。
まとめ
バリデーションは「HTMLの組み込み属性で土台を作る」ことが最重要です。required・type・min/max・step・pattern を適切に使えば、送信前に多くの誤入力を自動で防げます。JavaScript は checkValidity/reportValidity で検証の流れを制御し、足りない部分を setCustomValidity で補完。タイミングは入力中は軽く、確定後と送信前に強く。メッセージは具体的に、フォーカス誘導やアクセシビリティも考える。これらを徹底すれば、初心者でも短いコードで“優しくて壊れない”フォーム検証を実装できます。
