フォームを送信前に検証しよう
更新日:2026-08-02
前回は、フォーム部品の値をJavaScriptで取得しました。今回は、名前とメールアドレスを入力するフォームを作り、送信前に内容を検証します。
入力内容の検証はバリデーションとも呼ばれます。問題のある項目の近くへ理由を表示すると、利用者は何を直せばよいか判断できます。
この記事を終えると、次のことができるようになります。
submitイベントでフォーム全体を検証できるpreventDefault()で問題のある送信を止められる- 条件を関数へ分けて読みやすく整理できる
- エラーと成功の状態を画面へ伝えられる
- ブラウザ側とサーバー側の検証の違いを説明できる
今回使用するファイル構成
javascript-practice/
├── index.html
└── js/
└── script.js
検証するフォームを作る
HTMLを用意する
index.htmlへ次のフォームを記述します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>お問い合わせ</title>
</head>
<body>
<h1>お問い合わせ</h1>
<form id="contact-form" novalidate>
<div>
<label for="name">名前</label>
<input id="name" name="name" type="text" aria-describedby="name-error">
<p id="name-error" aria-live="polite"></p>
</div>
<div>
<label for="email">メールアドレス</label>
<input id="email" name="email" type="email" aria-describedby="email-error">
<p id="email-error" aria-live="polite"></p>
</div>
<button type="submit">送信内容を確認する</button>
</form>
<p id="form-message" aria-live="polite"></p>
<script src="js/script.js"></script>
</body>
</html>
novalidateは、ブラウザ標準のエラー表示を一時的に無効にする属性です。今回はJavaScriptによる検証結果を観察するために付けています。実際の制作では標準機能も活用できます。
aria-describedbyは入力欄とエラー文を関連付けます。aria-live="polite"を指定した要素の文章が変わると、スクリーンリーダーにも変更が伝わります。
送信時に入力値を検証する
必要な要素を取得する
js/script.jsへ、操作する要素を取得するコードを書きます。
const form = document.querySelector('#contact-form');
const nameInput = document.querySelector('#name');
const emailInput = document.querySelector('#email');
const nameError = document.querySelector('#name-error');
const emailError = document.querySelector('#email-error');
const formMessage = document.querySelector('#form-message');
変数名にInputやErrorを含めると、入力欄なのかエラー表示欄なのかを区別しやすくなります。
submitイベントを受け取る
続けて、フォーム送信を受け取ります。
form.addEventListener('submit', (event) => {
event.preventDefault();
console.log('検証を開始します');
});
submitイベントは、送信ボタンのクリックだけでなく、入力欄でEnterキーを押した場合にも発生します。そのためフォームの送信処理は、通常clickではなくsubmitで扱います。
名前の必須入力を確認する
イベント内で名前を取得し、空文字かどうかを調べます。
form.addEventListener('submit', (event) => {
event.preventDefault();
const name = nameInput.value.trim();
if (name === '') {
nameError.textContent = '名前を入力してください。';
} else {
nameError.textContent = '';
}
});
trim()を使うため、半角スペースだけの入力も未入力として扱えます。エラーがない場合に空文字を代入しているのは、前回表示したエラーを消すためです。
メールアドレスの形式を確認する
今回は学習用として、@が含まれているかを確認します。
const email = emailInput.value.trim();
if (email === '') {
emailError.textContent = 'メールアドレスを入力してください。';
} else if (!email.includes('@')) {
emailError.textContent = 'メールアドレスに@を含めてください。';
} else {
emailError.textContent = '';
}
includes('@')は、文字列に@が含まれていればtrueを返します。これはメールアドレスの完全な判定ではありません。実務ではHTMLのtype="email"、サーバー側の検証、確認メールなどを組み合わせます。
検証処理を関数へ分ける
項目が増えると、submitイベントの中が長くなります。項目ごとの検証を関数へ分けます。
エラーメッセージを返す関数を作る
問題がなければ空文字、問題があれば表示する文章を返す設計にします。
function validateName(name) {
if (name === '') {
return '名前を入力してください。';
}
if (name.length > 50) {
return '名前は50文字以内で入力してください。';
}
return '';
}
function validateEmail(email) {
if (email === '') {
return 'メールアドレスを入力してください。';
}
if (!email.includes('@')) {
return 'メールアドレスに@を含めてください。';
}
return '';
}
条件に該当した時点でreturnしているため、不要なelseを重ねずに読めます。この書き方を早期リターンと呼びます。
すべての検証結果をまとめる
js/script.jsを次の完成コードにします。
const form = document.querySelector('#contact-form');
const nameInput = document.querySelector('#name');
const emailInput = document.querySelector('#email');
const nameError = document.querySelector('#name-error');
const emailError = document.querySelector('#email-error');
const formMessage = document.querySelector('#form-message');
function validateName(name) {
if (name === '') {
return '名前を入力してください。';
}
if (name.length > 50) {
return '名前は50文字以内で入力してください。';
}
return '';
}
function validateEmail(email) {
if (email === '') {
return 'メールアドレスを入力してください。';
}
if (!email.includes('@')) {
return 'メールアドレスに@を含めてください。';
}
return '';
}
form.addEventListener('submit', (event) => {
event.preventDefault();
const name = nameInput.value.trim();
const email = emailInput.value.trim();
const nameErrorMessage = validateName(name);
const emailErrorMessage = validateEmail(email);
nameError.textContent = nameErrorMessage;
emailError.textContent = emailErrorMessage;
const hasError = nameErrorMessage !== '' || emailErrorMessage !== '';
if (hasError) {
formMessage.textContent = '入力内容を確認してください。';
if (nameErrorMessage !== '') {
nameInput.focus();
} else {
emailInput.focus();
}
return;
}
formMessage.textContent = '入力内容に問題はありません。';
});
hasErrorは、どちらか一方でもエラー文があればtrueになります。エラーがある場合は、最初に修正してほしい入力欄へfocus()でカーソルを移します。
動作を確認する
次の順番で試してください。
- 両方を空欄にして送信する
- 名前だけ入力して送信する
- メールアドレスへ
example.comと入力する - メールアドレスへ
user@example.comと入力する - 名前を50文字より長くして送信する
各条件に対応するエラーが項目の近くへ表示され、すべて正しければ成功メッセージが表示されます。
ブラウザ側とサーバー側の両方で検証する
JavaScriptの検証は、その場で結果を伝えられるため操作性を高めます。しかし、JavaScriptは無効化や改変が可能です。開発者が想定していないHTTPリクエストを直接送ることもできます。
そのため、データベースへの保存やメール送信を行う前には、Laravelなどのサーバー側でも必ず同じ入力条件を検証します。
- ブラウザ側:利用者へ早く分かりやすく伝える
- サーバー側:不正または壊れたデータを受け付けない
どちらか一方で代用するのではなく、役割の違う二つの検証として組み合わせます。
うまく動かないときの確認点
エラーが一度表示されると消えない
検証に成功した場合に、エラー表示へ空文字を設定しているか確認します。今回のコードでは、関数が成功時に''を返します。
event is not definedと表示される
コールバック関数の引数にeventがあるか確認してください。
form.addEventListener('submit', (event) => {
event.preventDefault();
});
正しく入力しても送信されない
今回の記事では、動作確認のため常にpreventDefault()を呼んでいます。実際にサーバーへ送信する段階では、エラーがない場合にform.submit()を呼ぶ方法や、fetch()で送る方法を設計します。
まとめ
- フォームの検証は
submitイベントで行う - 標準の送信を止めるときは
event.preventDefault()を使う - 項目ごとの検証を関数へ分けると条件を追いやすい
- エラーは原因が分かる文章で、対象項目の近くに表示する
- JavaScriptの検証だけに頼らず、サーバー側でも必ず検証する
次の記事では、同じ種類のボタンやカードが複数ある画面を扱います。querySelectorAll()とforEach()を使い、複数要素へまとめて処理を登録しましょう。