BLOG

バリデーション + 確認画面付きお問い合わせフォームを作ってみた

Written by sasaki

INDEX

前回の記事「SSGform + JavaScriptで確認画面付きフォームを作ってみた」では、基本的な確認画面の実装を紹介しました。 今回はその応用編として、実務で必須となる「バリデーション機能」を追加してみました。

SSGformの基本仕様については、公式サイトまたは前回の記事をご参照ください。

マークアップ作業

まずはベースとなるHTMLを作成します。今回も公式で提供されているコード例で「簡易スパム対策付きフォーム」を参考にして、フォームを作成します。
※簡易スパム対策はハニーポットフィールドと呼ばれる手法を使用します。
※reCAPTCHAはSSGForm側で設定可能です。

formタグのaction属性には、SSGformから発行された送信先URLを設定します。

<form class="form" id="contact-form" action="送信先URL" method="post">
 <div class="form__screen">
  <div style="display: none;"><input type="text" name="wana" /></div> <!-- name="wana"を見えない状態で設置 -->
  <div class="form__group">
   <label for="topic" class="form__heading">お問い合わせ項目<span class="form__heading-required">必須</span></label>
   <select id="topic" name="お問い合わせ項目" required>
  <option value="">選択してください</option>
    <option value="項目1">項目1</option>
    <option value="項目2">項目2</option>
   </select>
   <span class="form__error" id="error-topic"></span>
  </div>
  <div class="form__group">
   <label for="name" class="form__heading">お名前<span class="form__heading-required">必須</span></label>
   <input id="name" type="text" name="お名前" placeholder="お名前" required />
   <span class="form__error" id="error-name"></span>
  </div>
  <div class="form__group">
   <label for="email" class="form__heading">メールアドレス<span class="form__heading-required">必須</span></label>
   <input id="email" type="email" name="メールアドレス" placeholder="[email protected]" required/>
   <span class="form__error" id="error-email"></span>
  </div>
  <div class="form__group">
   <label for="message" class="form__heading">お問い合わせ内容<span class="form__heading-required">必須</span></label>
   <textarea id="message" name="お問い合わせ内容" placeholder="お問い合わせ内容" required></textarea>
   <span class="form__error" id="error-message"></span>
  </div>
  <button class="form__btn" id="form-confirm" type="button">確認画面</button>
 </div>
</form>

確認画面を挟んで送信を行うため、formタグ内に確認画面部分のコードを追加します。

<form class="form" id="contact-form" action="送信先URL" method="post">
 <!-- 入力画面の要素 -->
 (省略) 


 <!-- 確認画面の要素 -->
 <div id="confirmation-screen" class="confirmation" style="display: none;">
 <p>以下の内容で送信します。よろしいですか?</p>
 <div class="confirmation__group">
  <h3>お問い合わせ項目:</h3>
  <p id="confirm-topic"></p>
  </div>
  <div class="confirmation__group">
   <h3>お名前:</h3>
   <p id="confirm-name"></p>
  </div>
  <div class="confirmation__group">
   <h3>メールアドレス:</h3>
   <p id="confirm-email"></p>
  </div>
  <div class="confirmation__group">
   <h3>お問い合わせ内容:</h3>
   <p id="confirm-message"></p>
  </div>
  <div class="confirmation__btn-wrapper">
   <button class="confirmation__btn" id="confirm-back">戻る</button>
   <button class="confirmation__btn" id="confirm-submit" type="submit">送信する</button>
  </div>
 </div>
</form>

これでフォームの作成は完了です。
※画像では見やすくするため、スタイル調整を行っています。

JavaScriptによるバリデーションと確認画面の実装

今回、サニタイズ(無害化)のために「DOMPurifyを導入しています。
これは、悪意のあるユーザーが入力欄にスクリプトを混入させる「XSS(クロスサイトスクリプティング)攻撃」を防ぐためです。
コード内では textContent を使用しているため、基本的にはスクリプト注入のリスクは低いですが、単一の対策に頼るのではなく、多層防御の一環として「保険」のために「DOMPurify」を併用し、セキュリティを強化しました。

また「DOMPurifyの詳しい内容については、こちらの記事をご参照ください。

ライブラリのインストールします(npm経由)

npm install dompurify

インストールした「DOMPurify」を使用して実装していきます。

import DOMPurify from 'dompurify';

document.addEventListener('DOMContentLoaded', () => {
 const form = document.getElementById('contact-form'); // フォーム要素の参照
 const formWrap = document.querySelector('.form__screen'); // 入力画面の要素の参照
 const confirmationScreen = document.getElementById('confirmation-screen'); // 確認画面要素の参照
 const confirmTopic = document.getElementById('confirm-topic'); // 確認画面の各フィールドの参照(topic)
 const confirmName = document.getElementById('confirm-name'); // 確認画面の各フィールドの参照(name)
 const confirmEmail = document.getElementById('confirm-email'); // 確認画面の各フィールドの参照(email)
 const confirmMessage = document.getElementById('confirm-message'); // 確認画面の各フィールドの参照(message)
 const formConfirmButton = document.getElementById('form-confirm'); // フォームから確認画面に進むボタンの参照
 const confirmBack = document.getElementById('confirm-back'); // 確認画面からフォームへ戻るボタンの参照
 
 // エラーメッセージ要素を取得
 const errorTopic = document.getElementById('error-topic');
 const errorName = document.getElementById('error-name');
 const errorEmail = document.getElementById('error-email');
 const errorMessage = document.getElementById('error-message');

 // エラーメッセージの表示・非表示を切り替える関数
 const displayError = (errorElement, message) => {
  if (message) {
   errorElement.textContent = message;
   errorElement.style.display = 'block';
  } else {
   errorElement.textContent = '';
   errorElement.style.display = 'none';
  }
 }; 
 
 // フォームのバリデーション関数
 const validateForm = () => {
  let isValid = true; // バリデーション結果を保持するフラグ
  
  // フォームの入力値を取得 (trim() = 先頭と末尾にある空白文字を削除)
  const formData = {
   topic: form.elements['お問い合わせ項目'].value.trim(),
   name: form.elements['お名前'].value.trim(),
   email: form.elements['メールアドレス'].value.trim(),
   message: form.elements['お問い合わせ内容'].value.trim()
  };

  // 1. お問い合わせ項目のチェック
  if (formData.topic === '') {
   displayError(errorTopic, 'お問い合わせ項目を選択してください。');
   isValid = false;
  } else {
   displayError(errorTopic, '');
  }

  // 2. お名前のチェック
  if (formData.name === '') {
   displayError(errorName, 'お名前を入力してください。');
   isValid = false;
  } else {
   // 例: 20文字制限
   if (formData.name.length > 20) {
    displayError(errorName, 'お名前は20文字以内で入力してください。');
    isValid = false;
   } else { // 文字数制限クリア
    displayError(errorName, '');
   }
  }

  // 3. メールアドレスのチェック
  const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
  if (formData.email === '') {
   displayError(errorEmail, 'メールアドレスを入力してください。');
   isValid = false;
  } else if (!emailRegex.test(formData.email)) { // フォーマットチェック
   displayError(errorEmail, '有効なメールアドレス形式で入力してください。');
   isValid = false;
  } else {
   displayError(errorEmail, '');
  }

  // 4. お問い合わせ内容のチェック
  if (formData.message === '') {
   displayError(errorMessage, 'お問い合わせ内容を入力してください。');
   isValid = false;
  } else {
   displayError(errorMessage, '');
  }

  return { isValid, formData }; // バリデーション結果とフォームデータを返す
 };

 // 「確認画面」のボタンをクリックしたときの処理
 formConfirmButton.addEventListener('click', () => {
  const { isValid, formData } = validateForm();
  // もしバリデーションに失敗したら、確認画面に進まない
  if (!isValid) {
   return;
  }

  // 「DOMPurify」ライブラリでサニタイズする
  const sanitizedTopic = DOMPurify.sanitize(formData.topic);
  const sanitizedName = DOMPurify.sanitize(formData.name);
  const sanitizedEmail = DOMPurify.sanitize(formData.email);
  const sanitizedMessage = DOMPurify.sanitize(formData.message);

  // サニタイズした値を確認画面に表示
  confirmTopic.textContent = sanitizedTopic;
  confirmName.textContent = sanitizedName;
  confirmEmail.textContent = sanitizedEmail;
  confirmMessage.textContent = sanitizedMessage;

  // 入力フォームを非表示にして確認画面を表示
  formWrap.style.display = 'none'; // 入力フォームを非表示
  confirmationScreen.style.display = 'block'; // 確認画面を表示
  window.scrollTo({ top: 0, behavior: 'smooth' });
 });

 // 「戻る」ボタンをクリックしたとき
 confirmBack.addEventListener('click', (event) => {
  event.preventDefault(); // フォーム送信をキャンセル
  confirmationScreen.style.display = 'none'; // 確認画面を非表示
  formWrap.style.display = 'block'; // 入力フォームを表示
 });
};

未入力時のバリデーションとして空欄のまま「確認画面」をクリックすると、エラーメッセージが表示されます。

またメールアドレスの形式チェックとして不正な形式のメールアドレスを入力した場合も、エラーメッセージが出るようになりました。 


エラーがなければ確認画面へ遷移し、内容に問題がなければ送信が実行されます。

送信完了画面への遷移は前回の記事をご参照ください。

まとめ

今回はSSGformを使ってバリデーション + 確認画面付きのお問い合わせフォームを作成してみました。
バリデーションを加えることで、ユーザーの入力ミスを減らし、より実用的なお問い合わせフォームになります。

参考

https://ssgform.com/

https://ssgform.com/samples

https://qiita.com/kensiiwasaki/items/deb6d2ab6ba6b20308b6