SP用メニューボタン
ソレゾレブログ

技術的な事だったり日常の気になる事だったり

jQueryを使ってお問い合わせフォームのリアルタイムバリデーションチェックを自作する方法

スポンサーリンク

目次

はじめに

前回の記事ではお問い合わせフォームを自作する方法を書きましたが、今回の記事では入力値のバリデーションチェックを実装する方法を書いていきます。

この記事は2部構成となっております。

バリデーションチェックとは

バリデーションチェックとは、お問い合わせフォームの入力欄にユーザーが入力した値が仕様に則って正しく入力されているか、または、入力必須の項目に値が入力されているかなどをチェックすることを言います。

バリデーションチェックの実装方法

バリデーションチェックの実装方法としてはいくつか方法が挙げられますが、今回は、入力欄の値が変わった直後やデバイスの操作をイベント検知したら、ほぼリアルタイムにバリデーションチェックが走るパターンを実装していきます。

バリデーションの結果エラーであった場合は、リアルタイムにエラーメッセージを表示させたいので、ユーザーサイド言語のJavascript(jQuery)を使って実装していきます。全体的に省略していますが、コードはjQueryのreadyイベントの中に記載していきます。

スポンサーリンク

今回実装していくバリデーションチェックの内容

今回の例では、バリデーションチェックとあわせて、郵便番号から住所を自動入力する機能も実装していきます。実装する内容は下記となります。

  • 入力必須欄に値が入力されいるかチェックする。
  • 名前のふりがな欄に入力された値に平仮名以外の文字列を使っていないかチェックする。
  • メールアドレスの書式が標準仕様に則ったものになっているかチェックする。
  • メールアドレスと確認用メールアドレスがマッチしているかチェックする。
  • 郵便番号が7桁の数字で入力されているかチェックする。
  • すべてのバリデーションチェックがクリアするまでSubmitボタンを無効化する。
  • 郵便番号を入力すると住所が自動入力される。
  • エンターキーを押すとSubmitボタンが押されてしまうのを抑止する。

jQueryとCSSの準備

jQueryとスタイルシートの読み込み

jQueryのソースコードとカスタム作成したスクリプトを読み込ませます。

<head>
    <link rel="stylesheet" href="style.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="js/main.js"></script>
</head>

Wordpresを使っている場合は、function.phpに設定します。Wordpressに標準搭載されているjQueryを使う例です。

function my_add_files(){
    wp_enqueue_style('style', get_stylesheet_uri());
    wp_enqueue_script('jquery');
    wp_enqueue_script('main', esc_url( get_template_directory_uri() . '/js/main.js' ));
}

jQueryのReadyイベントを設定

これから書いていくコードは、全てjQueryのreadyイベントの中に書いていきます。readyイベントを設定していきます。

$(function(){
  ここにコードを書いていきます
});

WordPressの場合。

jQuery(function($){
  ここにコードを書いていきます
});

お問い合わせフォームの作成

それでは初めに、お問い合わせフォームのページから作成していきます。お問い合わせフォームは説明上わかりやすいように、必要最小限の要素とデザインに絞っています。デザインや要素(入力欄の種類)などは、ご自身の都合に合わせて作成カスタマイズしてください。

今回使うページのコードは下記です。ヘッダーとフッダーの部分は各自お好きなように作成してください。

<main class="contact">
    <h1>お問い合わせフォーム</h1>
    <form class="h-adr" action="/confirm" method="post" novalidate>
        <div class="input-text">
            <label for="yourname">氏名</label>
            <input type="text" name="yourname" id="yourname" required>
            <p class="msg required">※入力必須項目です</p>
        </div>
        <div class="input-text">
            <label for="furigana">ふりがな</label>
            <input type="text" name="furigana" id="furigana" required>
            <p class="msg required">※入力必須項目です</p>
            <p class="msg furigana">※ひらがなで入力してください</p>
        </div>
        <div class="input-email">
            <label for="email">メールアドレス</label>
            <input type="email" name="email" id="email" required>
            <p class="msg required">※入力必須項目です</p>
            <p class="msg email-format">※メールアドレスのフォーマットが正しくありません</p>
        </div>
        <div class="input-email">
            <label for="emailConfirm">メールアドレス(確認用)</label>
            <input type="email" name="emailConfirm" id="emailConfirm" required>
            <p class="msg required">※入力必須項目です</p>
            <p class="msg email-format">※メールアドレスのフォーマットが正しくありません</p>
            <p class="msg email-match">※メールアドレスが一致していません</p>
        </div>
        <div class="input-text">
            <label for="zipcode">郵便番号</label>
            <input class="p-postal-code" type="text" name="zipcode" id="zipcode">
            <p class="msg zipcode">※数字7桁で記入してください</p>
        </div>
        <div class="input-text">
            <label for="address">住所</label>
            <input class="p-region p-locality p-street-address p-extended-address " type="text" name="address" id="address">
        </div>
        <div class="input-text">
            <label for="building">建物</label>
            <input type="text" name="building" id="building">
        </div>
        <div class="textarea">
            <label for="textarea">お問い合わせ内容</label>
            <textarea name="text" id="text" rows="10" required></textarea>
            <p class="msg required">※入力必須項目です</p>
        </div>
        <div class="submit">
            <input type="submit" value="送信確認" class="confirm-button" id="confirmButton" disabled>
        </div>
        <span class="p-country-name" style="display:none;">Japan</span>
    </form>
</main>

msgクラスが付与されているp要素は、バリデーション結果によって表示されるエラーメッセージです。現時点ではわかりやすいようにすべて表示状態にしてありますが、実際の運用時は、チェック結果に合わせて表示非表示となるように作りこみます。メッセージの表示非表示はdisplayプロパティを使います。バリデーションの結果によってdisplayプロパティの設定してあるCSSクラスvisibleを対象メッセージの要素に追加削除して、メッセージを表示非表示させていきます。

メッセージを表示非表示する別の方法としては、メッセージの設定された要素をappend関数などを使って要素ごと差し込んだり削除したりという方法もありますが、今回は上記の方法を使っていきます。

入力必須項目のinput要素とtextareaには、入力必須項目であることがわかるようにrequired属性をあらかじめ設定しています。required属性を付与すると、ブラウザに標準装備されている入力必須項目のチェック機能が利用できるようになります。今回はブラウザ標準装備のバリデーションチェック機能は使わないので、novalidate属性をform要素に設定して無効化しています。

「送信確認」Submitボタンは、すべてのバリデーションチェックがクリアされたら押せる状態となります。初期状態ではdisabled属性が設定してあり、バリデーションがオールクリアとなったらdisabled属性を消してボタンを有効化します。

そのほかについては実装しながら説明していきます。

テスト用で適当に作ったのであまり参考にならないと思いますが、一応スタイルシートも載せておきます。

* {
  margin: 0;
  padding: 0;
}

body {
  padding: 40px;
  width: 100%;
}

main {
  width: 100%;
}

ul {
  text-decoration: none;
  list-style: none;
}

.contact h1 {
  margin-bottom: 20px;
}

form {
  width: 600px;
}

.input-checkbox {
  margin-bottom: 10px;
}

.input-checkbox .checkbox-title {
  display: inline-block;
  width: 200px;
}

.input-text,
.input-email,
.textarea {
  margin-bottom: 10px;
}

.input-text label,
.input-email label,
.textarea label {
  display: inline-block;
  width: 200px;
}

input[type="text"],
input[type="email"],
textarea {
  width: 300px;
}

.input-checkbox span {
  margin-left: 8px;
}

.input-checkbox span:nth-of-type(1) {
  margin-left: 0px;
}

.textarea label {
  vertical-align: top;
}

.form-item {
  width: 100%;
}

.submit {
  text-align: center;
}

.msg {
  margin-left: 210px;
  font-size: 14px;
  font-weight: bold;
  color: red;
}

設定したページを開くと下記のようになります。

お問い合わせフォームの作成1

エラーメッセージを非表示にするCSSの追加

現時点では、説明上わかりやすいようにエラーメッセージを全て表示させていますが、運用時は初期値で非表示となるようにします。表示にするか非表示にするかは、ページ初回読み込み時と入力欄の値変更時に実行するjQueryの関数によって判断操作させます。

初期値で非表示にするために、メッセージが設定されているすべてのp要素のクラスにdispaly: noneを設定していきます。

.msg.required,
.msg.furigana,
.msg.email-format,
.msg.email-match,
.msg.zipcode {
  display: none;
}

バリデーションチェックで問題なしとなったときにメッセージを非表示にするCSSも追加していききます。メッセージが設定されているp要素のクラスに、visibleクラスが追加されたらdispaly: noneによってメッセージが非表示となるようにします。

.msg.required.visible,
.msg.furigana.visible,
.msg.email-format.visible,
.msg.email-match.visible,
.msg.zipcode.visible{
  display: block;
}

この状態でページを再読み込みします。メッセージが非表示となっていると思います。Submitボタンは無効になっていてOKです。

エラーメッセージを非表示にするCSSの追加

バリデーションチェックの基本的な流れ

入力値のバリデーションチェックを設定していくにあたって、バリデーションチェックが呼び出される大まかな仕組みを説明します。

①キーボードとマウスが入力欄の値を変更をするイベント(デバイスが操作されたイベント)を検知させます。

②イベント検知後、バリデーションチェックを遅延させる関数を呼び出します。遅延処理用関数の役割は、ユーザビリティの向上です。対象デバイスが操作されるたびにバリデーションチェックが走っていたらユーザーとしては非常に鬱陶しいことになります。最後のデバイス操作から一定時間経ったタイミングでバリデーションチェックが走るように工夫します。

③最後のデバイス操作から遅延指定時間が経過してタイムアウトしたら、バリデーションチェック関数をコールバックします。

④バリデーションチェックの結果エラーとなった場合はエラーメッセージを表示し、クリアとなった場合はエラーメッセージを非表示化します。

⑤すべてのバリデーションチェックがクリアとなったらSubmitボタンを有効化します。

イベント検知と遅延処理関数の作成

イベント発生時に遅延処理をさせる為の関数作成

イベント発生後、バリデーションチェックの実行を遅延させるkeyupDelay関数を作成していきます。keyupDelay関数は、対象デバイスの操作をトリガーに呼び出されます。設定した時間が経過したら、バリデーションチェック用の関数をコールバックさせます。下記にコードを書いていきます。

function keyupDelay(callback, ms) {
	let timer = 0;
	return function() {
		let context = this;
		let args = arguments;
		clearTimeout(timer);
		timer = setTimeout(function () {
			callback.apply(context, args);
		}, ms);
	};
}

keyupDelay関数の第一引数は、遅延タイマーがタイムアウトした後にコールバックされる処理が格納されます。第二引数は、遅延時間が格納されます。

この関数はイベント発生のたびに呼び出されます。clearTimeoutによってsetTimeoutのタイマーが一旦リセットされ、その後setTimeoutが再度実行されます。タイムアウト前に再度対象デバイスが操作されると、タイマーがリセットされ、タイムアウトしない状態が続きます。これにより、バリデーションチェックがコールバックされず、ユーザー入力を邪魔しないという仕組みになります。

timer変数は、setTimeoutの返り値であるIDが格納されます。IDは、実行中のsetTimeoutを認識するために使われますが、clearTimeoutの引数にIDを設定して実行すると、対象のsetTimeoutのタイマーがリセットされます。

この処理については下記を参考にしました。

キーボードとマウス操作イベントを検知する関数の作成

デバイスの操作を検知しする関数を作成します。対象となるデバイス操作は、キーボードのキーをアップさせる、マウスでペースト、マウスでカット、となります。イベントを検知させるには、イベントを検知させたい要素(ユーザー入力対象のinput要素やtextarea要素)でonメソッドを呼び出します。コードは下記となります。

const requiredClassMulti = 'input[type="text"], input[type="email"], textarea';

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
 // keyupDelay関数によってコールバックされるバリデーションチェック処理をここに書いていく
}, 500));

1行目は、イベント検知の対象となる要素を配列に格納しています。対象要素は、textタイプのinput要素、emailタイプのinput要素、textarea要素、となります。

3行目は、対象要素でonメソッドを呼び出しています。第一引数にはイベントを設定します。対象とするイベントは、キーボードのキーを離す(keyup)、マウスでペースト(paste)、マウスでカット(cut)です。第二引数には、コールバック対象の遅延処理用関数(keyupDelay関数)を設定します。

コールバックされるkeyupDelay関数の第一引数には、keyupDelay関数による遅延処理がタイムアウトした後にコールバックされるバリデーションチェック処理を設定します。コールバックされるバリデーションチェック処理の作成方法は後述します。5行目の第二引数には、遅延時間をmsecで指定します。

これでOKです。

動作確認

onメソッド動作確認のために、コールバックされるバリデーションチェック処理部分にコンソールへのメッセージ出力を設定します。

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
 console.log('テスト成功'); // これを追記
}, 500));

お問い合わせフォームの必須入力欄で、キーボードのキーを押して離す、マウスでペースト、マウスでカット、の操作をして、下記のようにコンソールのメッセージが出力されればOKです。

イベント検知と遅延処理関数の作成

スポンサーリンク

バリデーションチェック関数の作成

入力必須項目に値が入力されているかチェックする

バリデーションチェック関数の作成

入力必須項目に値が入力されているかチェックする関数を作成していきます。必須入力項目のinput要素やtextarea要素をeach文で順番に呼び出し、バリデーションチェックでエラーとなった要素のエラーメッセージにvisibleクラスを追加して、エラーメッセージを表示させます。

const msgRequiredCls = '.msg.required'; 
const actNoDotsCls = 'visible';

function validation_required(targets) {
  $(targets).each(function(index, target){
     if ( $(target).attr('required') &&
        $(target).val() != '' &&
        $(target).siblings(msgRequiredCls).hasClass(actNoDotsCls) ) {
           $(target).siblings(msgRequiredCls).removeClass(actNoDotsCls);
     } else if ( $(target).attr('required') &&
        $(target).val() == '' &&
        !$(target).siblings(msgRequiredCls).hasClass(actNoDotsCls) ) {
           $(target).siblings(msgRequiredCls).addClass(actNoDotsCls);
     }
  })
}

1行目は、エラーメッセージ用のp要素に設定されているクラス名を、変数に格納しています。

2行目は、エラーメッセージを非表示にするためのクラス名visibleを変数に格納しています。

4行目は、関数を定義しています。引数の変数は、必須入力項目チェックのターゲットとなる要素が格納されます。

5行目は、対象要素を順番に呼び出しています。コールバック関数の第一引数は、each文のインデックス番号が格納されますが、これは使用しません。第二引数は、必須入力項目チェックのターゲット要素が格納されます。

6行目~8行目は、必須入力項目チェック結果OKとなった時にエラーメッセージを非表示にするための条件をif文で書いています。条件は、「対象要素にrequired属性が設定されている」且つ「入力欄が空欄ではない」 且つ 「エラーメッセージにvisibleクラスが存在する」となります。

9行目は、6行目~8行目がtrueとなった場合に実行されます。対象要素からvisibleクラスを削除しています。

10行目~12行目は、必須入力項目チェック結果NGとなった時にエラーメッセージを表示するための条件です。条件は、「対象要素にrequired属性が設定されている」且つ「入力欄が空欄」 且つ 「エラーメッセージにvisibleクラスが存在しない」となります。

13行目は、10行目~12行目がtrueとなった場合に実行されます。対象要素にvisibleクラスを追加しています。

イベントへの紐づけ

作成した関数を keyup paste cut イベントに紐づけます。

const requiredClassAry = ['input[type="text"]', 'input[type="email"]', 'textarea'];

$(requiredClassAry).each(function(index, target){
   validation_required(target);
});

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
   $(requiredClassAry).each(function(index, target){
      validation_required(target);
   });
}, 500));

1行目は、対象デバイスが操作された時にイベントを発動させたい要素を変数に格納しています。対象要素は、input要素のtextタイプ、input要素のemailタイプ、textarea要素、と複数となりますので、配列変数を使います

3行目は、each文で必須入力項目チェックの対象要素を順番に呼び出しています。コールバック関数の第一引数は、インデックス番号です。ここでもこれは使いません。第二引数は、対象要素が格納されます。

4行目は、先述作成した必須入力項目のバリデーションチェック関数です。第一引数には、each文の第二引数に設定したtarget変数(必須入力項目チェックの対象要素)を渡してあげます。

3行目~5行目のコードはページを読み直後に実行させたい為、readyイベントの直下に書いています。ページ読み込み時はエラーメッセージが全て非表示となっているので、メッセージを表示させてユーザーに入力を促すために、バリデーションチェックを即時実行させます。

8行目~10行目は、3行目~5行目のコードと同じです。説明は割愛します。

動作確認

動作確認をしていきます。必須項目に値を入力したり削除したりしてみてください。赤文字の「※入力必須項目です」が非表示になったり表示されたりすると思います。

入力必須項目に値が入力されているかチェックする1

必須入力項目に値を入力するとエラーメッセージが非表示になります。

入力必須項目に値が入力されているかチェックする2

「ふりがな」欄に平仮名だけが入力されているかチェックする

バリデーションチェック関数の作成

「ふりがな」欄は平仮名のみで入力してほしいので、平仮名以外を入力した場合はエラーメッセージを表示するよう作りこみます。コードは下記です。

const msgFuriganaCls = '.msg.furigana';
const regFurigana = /^[\u3040-\u309f]+$/;

function validation_furigana(target) {
  furiganaValue = $(target).val();
  if ( furiganaValue != '' && regFurigana.test(furiganaValue) || furiganaValue == '' ) {
     if ( $(msgFuriganaCls).hasClass(actNoDotsCls) ) {
        $(msgFuriganaCls).removeClass(actNoDotsCls);
     }
  } else if ( furiganaValue != '' && !regFurigana.test(furiganaValue) ) {
     if ( !$(msgFuriganaCls).hasClass(actNoDotsCls) ) {
        $(msgFuriganaCls).addClass(actNoDotsCls);
     }
  }
}

1行目は、エラーメッセージ表示用要素(p要素)のクラス名を変数に格納しています。

2行目は、ふりがなを表す正規表現を変数に格納しています。

4行目は、関数を定義しています。第一引数には、関数実行時に第一引数に渡す値(ふりがな入力欄のid)が格納されます。

5行目は、ふりがな欄に入力された値を取得しています。

6行目は、エラーメッセージを非表示にするための条件を設定しています。条件は「ふりがな欄に値が入力されている」且つ「ふりがな欄の値が平仮名のみで入力されている」、もしくは「ふりがな欄が空欄」となります。

7行目も6行目に引き続き、エラーメッセージを非表示にするための条件判定をしています。7行目の条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されている」という条件となります。

8行目は、6行目~7行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを削除してエラーメッセージを非表示にさせます。

10行目は、エラーメッセージを表示するための条件を設定しています。条件は「ふりがな欄に値が入力されている」且つ「ふりがな欄の値が平仮名のみで入力されていない」となります。

11行目も10行目に引き続き、エラーメッセージを表示するための条件判定をしています。11行目の条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されていない」という条件となります。

12行目は、10行目~11行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを追加してエラーメッセージを表示させます。

イベントへの紐づけ

作成した関数を keyup paste cut イベントに紐づけます。

const furiganaID = '#furigana';

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
       :
   validation_furigana(furiganaID);
}, 500));

1行目は、ふりがなの入力欄であるinput要素のid名を取得しています。

5行目は、先述作成したふりがな欄に平仮名のみが入力されているかバリデーションチェックする関数を設定します。第一引数には、ふりがな入力欄のidを格納した変数を渡します。

動作確認

動作確認をしていきます。必須入力項目となっているので何も入力されていない時は、「※入力必須項目です」が表示されています。

「ふりがな」欄に平仮名だけが入力されているかチェックする1

ふりがな欄に平仮名だけの値を入力てください。エラーが非表示になります。

「ふりがな」欄に平仮名だけが入力されているかチェックする2

平仮名以外を入力してください。エラーメッセージが表示されます。

「ふりがな」欄に平仮名だけが入力されているかチェックする3

メールアドレスの書式をチェックする

メールアドレスのフォーマット

ローカルパートのフォーマット

このほかにもいくつかルールがありますが、全て組み込むと大変複雑になってしまうので、いくつかに絞って実装していきます。

  • 使える記号はハイフン「-」、アンダースコア[_]、ピリオド「.」のみ
  • 最初と最後にピリオド「.」は使用できない
  • 大文字小文字の英字と数字が使用可能(大文字小文字は実際区別されない。大文字を使っている例は実際見ない。)
  • 1文字目は英字または数字のみ

ドメインのフォーマット

  • 使える文字は大文字小文字の英字と数字、そしてハイフン「-」のみ(大文字小文字の英字は実際区別されない。大文字を使っている例は実際見ない。)
  • トップレベル、第2レベル、第3レベル、第4レベルのドメイン(ラベル)があり、それぞれはピリオド「.」によって区切られる。(各レベルの文字列はラベルと言うそう)
  • 最初と最後ににハイフン「-」は使用できない

第4レベルのドメインはコンピュータ名などを表しますが、メールアドレスのドメインでこのレベルを使っていることはあまり見ないので、第4レベルのドメインチェックは省略します。

文字数の制限

メールアドレスの文字数は、RFC5321の4.5.3.1「Size Limits and Minimums」で定義されています。ローカルパートは最大64文字、ドメインは最大ピリオドを含め253文字(一つのラベルの長さは63文字以下)、メールアドレス全体で最大254文字と定義されています。

そのように定義されていますが、今回のバリデーションチェックではチェックしません。必要な方は個別にカスタマイズしてください。

バリデーションチェック関数の作成

メールアドレスの書式バリデーションチェック関数を作成していきます。メールアドレスの書式は、先述したルールをチェックしていきます。もっと厳密にされたい方は、個別にカスタマイズしてください。

チェックする内容ごとに、チェック用の正規表現コードを作成していきます。

①ローカルパートの最初の1文字は、英字の大文字小文字、数字
^[A-Za-z0-9]{1}

②ローカルパートの2文字目から最後から2番目の文字は、英字の大文字小文字、数字、ハイフン「-」、アンダースコア[_]、ピリオド「.」
[A-Za-z0-9_.-]{1,}

③ローカルパートの最後の1文字は、英字の大文字小文字、数字
[A-Za-z0-9]{1}

④ローカルパートとドメインの間はアットマーク「@」1文字
@{1}

⑤第3レベルドメインの最初の1文字は、英字の大文字小文字、数字
[A-Za-z0-9]{1}

⑥第3レベルドメインの2文字目から最後から2番目の文字は、英字の大文字小文字、数字、ハイフン「-」
[A-Za-z0-9-]{1,}

⑦第3レベルドメインの最後の1文字は、英字の大文字小文字、数字
[A-Za-z0-9]{1}

⑧第2レベルドメインの直前にピリオド「.」0文字以上
\.*

⑨第2レベルドメインの文字列は、英字の大文字小文字、数字、0文字以上
[A-Za-z0-9]*

⑩第1レベルドメインの直前にピリオド「.」1文字以上
\.

⑪第1レベルドメインはメールアドレスの最後に存在し、文字列は、英字の大文字小文字、数字、1文字以上
[A-Za-z0-9]{1,}$

正規表現のパーツが出来上がったので、バリデーションチェック関数を作成していきます。

const msgEmailFormatCls = '.msg.email-format';
const regEmail = /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]{1,}[A-Za-z0-9]{1}@{1}[A-Za-z0-9]{1}[A-Za-z0-9-]{1,}[A-Za-z0-9]{1}\.*[A-Za-z0-9]*\.[A-Za-z0-9]{1,}$/;

function validation_email_format(target) {
  let emailValue = $(target).val();
  if ( emailValue != '' && regEmail.test(emailValue) ) {
     if ( $(target).siblings(msgEmailFormatCls).hasClass(actNoDotsCls) ) {
        $(target).siblings(msgEmailFormatCls).removeClass(actNoDotsCls);
     }
  } else if ( emailValue != '' && !regEmail.test(emailValue) ) {
     if ( !$(target).siblings(msgEmailFormatCls).hasClass(actNoDotsCls) ) {
        $(target).siblings(msgEmailFormatCls).addClass(actNoDotsCls);
     }
  }
}

1行目は、エラーメッセージ表示用要素(p要素)のクラス名を変数に格納しています。

2行目は、メールアドレスの書式をチェックするための正規表現を変数に格納しています。先ほど作った正規表現のパーツを①から順につなげています。最初と最後のスラッシュ「/」は、間に書いてある文字が正規表現であることを表しています。

4行目は、関数を定義しています。第一引数には、関数実行時に第一引数に渡す値(メールアドレスおよび確認用入力欄のid)が格納されます。

5行目は、メールアドレス欄に入力された値を取得しています。

6行目は、エラーメッセージを非表示にするための条件判定しています。条件は「メールアドレス入力欄が空欄ではない」且つ「メールアドレスが書式に則っているか」となります。test関数は、対象の値が引数に設定した正規表現とマッチするかチェックする関数です。

7行目も6行目に引き続き、エラーメッセージを非表示にするための条件判定をしています。7行目の条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されている」という条件となります。

8行目は、6行目~7行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを削除してエラーメッセージを非表示にさせます。

10行目は、エラーメッセージを表示するための条件判定をしています。条件は「メールアドレス入力欄に値が入力されている」且つ「メールアドレスが書式に則っていない」となります。

11行目も10行目に引き続き、エラーメッセージを表示するための条件判定をしています。11行目の条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されていない」という条件となります。

12行目は、10行目~11行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを追加してエラーメッセージを表示させます。

イベントへの紐づけ

作成した関数を keyup paste cut イベントに紐づけます。

const emailID = '#email';
const emailConfirmID = '#emailConfirm';

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
       :
   validation_email_format(emailID);
   validation_email_format(emailConfirmID);
}, 500));

1~2行目は、メールアドレス欄とメールアドレス確認用欄の、input要素id名を取得しています。

6~7行目は、メールアドレス書式チェック用の関数です。第一引数には、メールアドレス入力および確認用欄のidを格納した変数を渡します。

動作確認

動作確認をしていきます。正規表現に設定した書式にマッチしないメールアドレスを入力してみてください。アットマークを抜いてみたり、メールアドレスの頭にハイフンを付けてみたり、いろいろ試してみてください。確認欄にも同じように入力してみてください。書式に反するメールアドレスを入力した時だけ下記のようにエラーメッセージが表示されればOKです。

メールアドレスの書式をチェックする1

正しい書式のメールアドレスを入力した時は、エラーが非表示となります。

メールアドレスの書式をチェックする2

スポンサーリンク

メールアドレスと確認用メールアドレスの合致確認

バリデーションチェック関数の作成

メールアドレスの合致確認は単純です。メールアドレスと確認用メールアドレスをif文で比較します。下記にコードを記載します。

const msgEmailMatchCls = '.msg.email-match';

function validation_email_match(target1, target2) {
  let emailValue = $(target1).val();
  let emailConfirmValue = $(target2).val();
  if ( emailValue != '' && emailConfirmValue != '' && emailValue == emailConfirmValue || emailConfirmValue == '' ) {
     if ( $(msgEmailMatchCls).hasClass(actNoDotsCls) ) {
        $(msgEmailMatchCls).removeClass(actNoDotsCls);
     }
  } else {
     if ( !$(msgEmailMatchCls).hasClass(actNoDotsCls) ) {
        $(msgEmailMatchCls).addClass(actNoDotsCls);
     }
  }
}

1行目は、エラーメッセージ表示用要素(p要素)のクラス名を変数に格納しています。

3行目は、関数を定義しています。第一引数には、関数実行時に第一引数に渡す値(メールアドレス入力欄のid)が格納されます。第二引数には、関数実行時の第二引数に渡した対象要素(メールアドレス確認用入力欄のid)が格納されます。

4行目は、メールアドレス欄に入力された値を取得しています。

5行目は、メールアドレス確認欄に入力された値を取得しています。

6行目は、エラーメッセージを非表示にするための条件判定しています。条件は「メールアドレス欄が空欄ではない」且つ「確認用メールアドレス欄が空欄ではない」且つ「メールアドレスと確認用メールアドレスの文字列が合致するか」、もしくは「確認用メールアドレスが空欄」となります。確認用メールアドレス欄が空欄であった場合は、メールアドレス欄に値があってもエラーは非表示にします。

7行目も6行目に引き続き、エラーメッセージを非表示にするための条件判定をしています。7行目の条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されている」という条件となります。

8行目は、6行目~7行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを削除してエラーメッセージを非表示にさせます。

10行目以降は、6行目でfalseだった場合に実行されます。

11行目は、エラーメッセージを表示するための条件判定をしています。条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されていない」という条件となります。

12行目は、11行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを削除してエラーメッセージを非表示にさせます。

イベントへの紐づけ

作成した関数を keyup paste cut イベントに紐づけます。

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
       :
   validation_email_match(emailID, emailConfirmID);
}, 500));

3行目に、メールアドレス合致確認用の関数を追加します。第一引数には、メールアドレス入力欄のidを格納した変数、第二引数には、確認用メールアドレス入力欄のidを格納した変数を設定します。

動作確認

メールアドレスと確認用メールアドレス欄に、異なるアドレスを入力してください。非一致の場合は、下記のように確認用の欄だけにエラーが表示されます。

メールアドレスと確認用メールアドレスの合致確認1

郵便番号を数字7桁以外で入力するとエラー表示させる

バリデーションチェック関数の作成

郵便番号欄に、7桁の数字以外を入力した場合にエラーメッセージを出力させます。バリデーションチェック用の関数を作成していきます。コードは下記です。

const msgZipcodeCls = '.msg.zipcode';
const regZipcode = /^\d{7}$/;

function validation_zipcode(target) {
  let zipcodeValue = $(target).val(); 
  if ( regZipcode.test(zipcodeValue) && zipcodeValue != '' || zipcodeValue == '' ) {
     if ( $(msgZipcodeCls).hasClass(actNoDotsCls) ) {
        $(msgZipcodeCls).removeClass(actNoDotsCls);
     }
  } else if ( !regZipcode.test(zipcodeValue) && zipcodeValue != '' ) {
     if ( !$(msgZipcodeCls).hasClass(actNoDotsCls) ) {
        $(msgZipcodeCls).addClass(actNoDotsCls);
     }
  }
}

1行目は、エラーメッセージ表示用要素(p要素)のクラス名を変数に格納しています。

2行目は、郵便番号の書式をチェックするための正規表現を変数に格納しています。

4行目は、関数を定義しています。第一引数には、関数実行時に第一引数に渡す値(郵便番号入力欄のid)が格納されます。

5行目は、郵便番号欄に入力された値を取得しています。

6行目は、エラーメッセージを非表示にするための条件判定しています。条件は「郵便番号欄に入力した値が7桁の数字である」且つ「郵便番号欄が空欄ではない」となります。郵便番号は必須入力欄ではないので、空欄であった場合はエラーを非表示にします。

7行目も6行目に引き続き、エラーメッセージを非表示にするための条件判定をしています。7行目の条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されている」という条件となります。

8行目は、6行目~7行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを削除してエラーメッセージを非表示にさせます。

10行目は、エラーメッセージを表示するための条件判定をしています。条件は「郵便番号欄に入力した値が7桁の数字ではない」且つ「郵便番号欄が空欄ではない」となります。

11行目も10行目に引き続き、エラーメッセージを表示するための条件判定をしています。11行目の条件は、「エラーメッセージ表示用のvisibleクラスが既にメッセージ表示用のp要素に設定されていない」という条件となります。

12行目は、10行目~11行目の条件判定でtrueだった場合に実行されます。エラーメッセージ用のvisibleクラスを追加してエラーメッセージを表示させます。

イベントへの紐づけ

作成した関数を keyup paste cut イベントに紐づけます。

const zipcodeID = '#zipcode';

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
       :
   validation_zipcode(zipcodeID);
}, 500));

1行目は、 郵便番号欄のinput要素id名を取得しています。

5行目は、郵便番号書式チェック用の関数を追加します。第一引数には、郵便番号入力欄のidを格納した変数を設定します。

動作確認

郵便番号欄に、7桁の数字もしくは、7桁の数字以外の文字列を入力してください。エラーの場合は下記メッセージが出力されます。

を数字7桁以外で入力するとエラー表示させる

バリデーションオールクリア判定及びSubmitボタン有効化無効化

判定関数の作成

バリデーションチェックがオールクリアとなったら、Submitボタンを有効化させます。エラーメッセージが表示されている時は、エラーメッセージ用のp要素にvisibleクラスが付与されていますので、ページ内にvisibleクラスが0個になったらオールクリア判定、1個以上あったらノークリア判定とします。コードは下記となります。

const actCls = '.visible';

function validation_submit_disabled(target) {
  if( $(actCls).length && !$(target).prop('disabled') ){
     $(target).prop('disabled', true);
  } else if ( !$(actCls).length && $(target).prop('disabled') ){
     $(target).prop('disabled', false);
  }
}

1行目は、visibleクラスを変数に格納しています。こちらはピリオド(ドット)付で格納します。

3行目は、関数を定義しています。第一引数には、関数実行時に第一引数に渡す値(submitボタンのid名)が格納されます。

4行目は、Sbumitボタンを無効化するための判定をしています。条件は、「ページ内にvisibleクラスが1個以上存在する」且つ「Submitボタンのinput要素にdisabled属性が設定されていない」となります。

5行目は、4行目の条件判定でtrueとなった時の処理です。Submitボタンのinput要素にdisabled属性を設定して無効化します。

6行目は、Sbumitボタンを有効化するための判定をしています。条件は、「ページ内にvisibleクラスが1個以上存在しない(0個)」且つ「Submitボタンのinput要素にdisabled属性が設定されている 」となります。

7行目は、6行目の条件判定でtrueとなった時の処理です。Submitボタンのinput要素からdisabled属性を削除して有効化します。

イベントへの紐づけ

作成した関数を keyup paste cut イベントに紐づけます。

const submitBtnID = '#confirmButton';

$(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
       :
   validation_submit_disabled(submitBtnID);
}, 500));

1行目は、Submitボタンのinput要素id名を取得しています。

5行目は、バリデーションオールクリア判定及びSubmitボタン有効化無効化用の関数です。第一引数には、Submitボタンのinput要素のidを格納した変数を渡します。

動作確認

動作確認をしていきます。必須入力項目をすべて入力し、これまで設定してきたバリデーションチェックをクリアしてください。そうすれば、Submitボタンが有効化され押せるようになります。画像上段が無効化状態、下段が有効化状態のボタンです。

バリデーションオールクリア判定及びSubmitボタン有効化無効化1
バリデーションオールクリア判定及びSubmitボタン有効化無効化2

郵便番号から住所を自動入力させる方法

郵便番号と住所の紐づけを自分で管理し続けるのは至難の業です。流石に自分で管理するのは難しいということで、便利なプラグインを利用することにします。

yubinbango.js

このページのサンプルにyubinbangoを設定してみます。まずは、yubinbago.jsを読み込むためのスクリプトをhead要素内に書きます。

<head>
    <script src="https://yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script>
</head>

次に、yubinbago.jsを動かすform要素に「h-adr」クラスを追加します。

<form class="h-adr" action="/confirm" method="post" novalidate>

form内のどこでもいいので、下記のコードを記載します。書き方はどちらか一つで良いそうです。

<span class="p-country-name" style="display:none;">Japan</span>
  もしくは
<input type="hidden" class="p-country-name" value="Japan">

郵便番号入力欄に「p-postal-code」クラスを追加します。

<input class="p-postal-code" type="text" name="zipcode" id="zipcode">

住所を出力する欄のクラスに、都道府県名(p-region)、市町村区(p-locality)、町域(p-street-address)、以降の住所(p-extended-address) を追加します。私の例では全て一要素におさめていますが、出力する要素を分けたい場合は枠毎にクラスを設定する必要があります。

<input class="p-region p-locality p-street-address p-extended-address" type="text" name="address" id="address">

これで完成です。郵便番号を入力すると、住所が自動的に入力されたかと思います。

スポンサーリンク

Enterキーを押したときにフォームのSubmitが実行されるのを無効にする

お問い合わせ入力中にEnterキーを押して、変換確定後にもう一回Enterキーを押してしまうとSubmitボタンが実行されてしまいます。とても鬱陶しいので、これを無効にします。コードは下記です。

$('input').not($('input[type="button"],input[type="submit"]')).keypress(function (event) {
  if (!event) var event = window.event;
  if (event.keyCode == 13)
     return false;
});

最終的に出来上がったコード

参考までに、最終的に出来上がったコードを載せておきます。

<!DOCTYPE html>
<html lang="jp">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="js/main.js"></script>
    <script src="https://yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script>
</head>
<body>
<main class="contact">
    <h1>お問い合わせフォーム</h1>
    <form class="h-adr" action="/confirm" method="post" novalidate>
        <div class="input-text">
            <label for="yourname">氏名</label>
            <input type="text" name="yourname" id="yourname" required>
            <p class="msg required">※入力必須項目です</p>
        </div>
        <div class="input-text">
            <label for="furigana">ふりがな</label>
            <input type="text" name="furigana" id="furigana" required>
            <p class="msg required">※入力必須項目です</p>
            <p class="msg furigana">※ひらがなで入力してください</p>
        </div>
        <div class="input-email">
            <label for="email">メールアドレス</label>
            <input type="email" name="email" id="email" required>
            <p class="msg required">※入力必須項目です</p>
            <p class="msg email-format">※メールアドレスのフォーマットが正しくありません</p>
        </div>
        <div class="input-email">
            <label for="emailConfirm">メールアドレス(確認用)</label>
            <input type="email" name="emailConfirm" id="emailConfirm" required>
            <p class="msg required">※入力必須項目です</p>
            <p class="msg email-format">※メールアドレスのフォーマットが正しくありません</p>
            <p class="msg email-match">※メールアドレスが一致していません</p>
        </div>
        <div class="input-text">
            <label for="zipcode">郵便番号</label>
            <input class="p-postal-code" type="text" name="zipcode" id="zipcode">
            <p class="msg zipcode">※数字7桁で記入してください</p>
        </div>
        <div class="input-text">
            <label for="address">住所</label>
            <input class="p-region p-locality p-street-address p-extended-address " type="text" name="address" id="address">
        </div>
        <div class="input-text">
            <label for="building">建物</label>
            <input type="text" name="building" id="building">
        </div>
        <div class="textarea">
            <label for="textarea">お問い合わせ内容</label>
            <textarea name="text" id="text" rows="10" required></textarea>
            <p class="msg required">※入力必須項目です</p>
        </div>
        <div class="submit">
            <input type="submit" value="送信確認" class="confirm-button" id="confirmButton" disabled>
        </div>
        <span class="p-country-name" style="display:none;">Japan</span>
    </form>
</main>
</body>
</html>
* {
  margin: 0;
  padding: 0;
}

body {
  padding: 40px;
  width: 100%;
}

main {
  width: 100%;
}

ul {
  text-decoration: none;
  list-style: none;
}

.contact h1 {
  margin-bottom: 20px;
}

form {
  width: 600px;
}

.input-checkbox {
  margin-bottom: 10px;
}

.input-checkbox .checkbox-title {
  display: inline-block;
  width: 200px;
}

.input-text,
.input-email,
.textarea {
  margin-bottom: 10px;
}

.input-text label,
.input-email label,
.textarea label {
  display: inline-block;
  width: 200px;
}

input[type="text"],
input[type="email"],
textarea {
  width: 300px;
}

.input-checkbox span {
  margin-left: 8px;
}

.input-checkbox span:nth-of-type(1) {
  margin-left: 0px;
}

.textarea label {
  vertical-align: top;
}

.form-item {
  width: 100%;
}

.submit {
  text-align: center;
}

.msg {
  margin-left: 210px;
  font-size: 14px;
  font-weight: bold;
  color: red;
}

.msg.required,
.msg.furigana,
.msg.email-format,
.msg.email-match,
.msg.zipcode {
  display: none;
}

.msg.required.visible,
.msg.furigana.visible,
.msg.email-format.visible,
.msg.email-match.visible,
.msg.zipcode.visible{
  display: block;
}
jQuery(function($){

   const furiganaID = '#furigana';
   const emailID = '#email';
   const emailConfirmID = '#emailConfirm';
   const zipcodeID = '#zipcode';
   const submitBtnID = '#confirmButton';

   const actCls = '.visible';
   const actNoDotsCls = 'visible';
   const msgRequiredCls = '.msg.required';
   const msgFuriganaCls = '.msg.furigana';
   const msgEmailFormatCls = '.msg.email-format';
   const msgEmailMatchCls = '.msg.email-match';
   const msgZipcodeCls = '.msg.zipcode';

   const regFurigana = /^[\u3040-\u309f]+$/;
   const regEmail = /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]{1,}[A-Za-z0-9]{1}@{1}[A-Za-z0-9]{1}[A-Za-z0-9-]{1,}[A-Za-z0-9]{1}\.*[A-Za-z0-9]*\.[A-Za-z0-9]{1,}$/;
   const regZipcode = /^\d{7}$/;

   const requiredClassAry = ['input[type="text"]', 'input[type="email"]', 'textarea'];
   const requiredClassMulti = 'input[type="text"], input[type="email"], textarea';

   function validation_required(targets) {
      $(targets).each(function(index, target){
         if ( $(target).attr('required') &&
            $(target).val() != '' &&
            $(target).siblings(msgRequiredCls).hasClass(actNoDotsCls) ) {
               $(target).siblings(msgRequiredCls).removeClass(actNoDotsCls);
         } else if ( $(target).attr('required') &&
            $(target).val() == '' &&
            !$(target).siblings(msgRequiredCls).hasClass(actNoDotsCls) ) {
               $(target).siblings(msgRequiredCls).addClass(actNoDotsCls);
         }
      })
   }

   function validation_furigana(target) {
      furiganaValue = $(target).val();
      if ( furiganaValue != '' && regFurigana.test(furiganaValue) || furiganaValue == '' ) {
         if ( $(msgFuriganaCls).hasClass(actNoDotsCls) ) {
            $(msgFuriganaCls).removeClass(actNoDotsCls);
         }
      } else if ( furiganaValue != '' && !regFurigana.test(furiganaValue) ) {
         if ( !$(msgFuriganaCls).hasClass(actNoDotsCls) ) {
            $(msgFuriganaCls).addClass(actNoDotsCls);
         }
      }
   }

   function validation_email_format(target) {
      let emailValue = $(target).val();
      if ( emailValue != '' && regEmail.test(emailValue) ) {
         if ( $(target).siblings(msgEmailFormatCls).hasClass(actNoDotsCls) ) {
            $(target).siblings(msgEmailFormatCls).removeClass(actNoDotsCls);
         }
      } else if ( emailValue != '' && !regEmail.test(emailValue) ) {
         if ( !$(target).siblings(msgEmailFormatCls).hasClass(actNoDotsCls) ) {
            $(target).siblings(msgEmailFormatCls).addClass(actNoDotsCls);
         }
      }
    }

   function validation_email_match(target1, target2) {
      let emailValue = $(target1).val();
      let emailConfirmValue = $(target2).val();
      if ( emailValue != '' && emailConfirmValue != '' && emailValue == emailConfirmValue || emailConfirmValue == '' ) {
         if ( $(msgEmailMatchCls).hasClass(actNoDotsCls) ) {
            $(msgEmailMatchCls).removeClass(actNoDotsCls);
         }
      } else {
         if ( !$(msgEmailMatchCls).hasClass(actNoDotsCls) ) {
            $(msgEmailMatchCls).addClass(actNoDotsCls);
         }
      }
   }

   function validation_zipcode(target) {
      let zipcodeValue = $(target).val(); 
      if ( regZipcode.test(zipcodeValue) && zipcodeValue != '' || zipcodeValue == '' ) {
         if ( $(msgZipcodeCls).hasClass(actNoDotsCls) ) {
            $(msgZipcodeCls).removeClass(actNoDotsCls);
         }
      } else if ( !regZipcode.test(zipcodeValue) && zipcodeValue != '' ) {
         if ( !$(msgZipcodeCls).hasClass(actNoDotsCls) ) {
            $(msgZipcodeCls).addClass(actNoDotsCls);
         }
      }
   }

   function validation_submit_disabled(target) {
      if( $(actCls).length && !$(target).prop('disabled') ){
         $(target).prop('disabled', true);
      } else if ( !$(actCls).length && $(target).prop('disabled') ){
         $(target).prop('disabled', false);
      }
   }

   function keyupDelay(callback, ms) {
      let timer = 0;
      return function() {
         let context = this;
         let args = arguments;
         clearTimeout(timer);
         timer = setTimeout(function () {
            callback.apply(context, args);
         }, ms);
      };
   }

   $(requiredClassAry).each(function(index, target){
      validation_required(target);
   });

   $(requiredClassMulti).on('keyup paste cut', keyupDelay(function () {
      // console.log('テスト成功');
      $(requiredClassAry).each(function(index, target){
         validation_required(target);
      });
      validation_furigana(furiganaID);
      validation_email_format(emailID);
      validation_email_format(emailConfirmID);
      validation_email_match(emailID, emailConfirmID);
      validation_zipcode(zipcodeID);
      validation_submit_disabled(submitBtnID);
   }, 500));

   $('input').not($('input[type="button"],input[type="submit"]')).keypress(function (event) {
      if (!event) var event = window.event;
      if (event.keyCode == 13)
         return false;
   });
});

Submitボタンをクリック後一括でバリデーションチェックを実行する方法

下記に記事をおこしましたのでご覧ください。

jQueryを使ってお問い合わせフォームのバリデーションチェックをsubmit後に実行する方法