本文へスキップ
JavaScript 基礎 JavaScript フォーム バリデーション

フォームを送信前に検証しよう

更新日: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');

変数名にInputErrorを含めると、入力欄なのかエラー表示欄なのかを区別しやすくなります。

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()でカーソルを移します。

動作を確認する

次の順番で試してください。

  1. 両方を空欄にして送信する
  2. 名前だけ入力して送信する
  3. メールアドレスへexample.comと入力する
  4. メールアドレスへuser@example.comと入力する
  5. 名前を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()を使い、複数要素へまとめて処理を登録しましょう。