JavaScriptフォーム検証

概要: このチュートリアルでは、サインアップフォームをゼロから構築することで、JavaScriptフォーム検証について学びます。

フォーム検証とは

サーバーにデータを送信する前に、Webブラウザでデータをチェックして、送信されたデータが正しい形式であることを確認する必要があります(サーバーへのデータ送信を参照)。

迅速なフィードバックを提供するために、JavaScriptを使用してデータを検証できます。これはクライアントサイド検証と呼ばれます。

クライアントサイド検証を実行しないと、ユーザーエクスペリエンスが低下する可能性があります。この場合、フォームデータがWebブラウザとサーバー間で転送されるのに時間がかかるため、顕著な遅延を感じる場合があります。

Webブラウザで実行されるクライアントサイド検証とは異なり、サーバーサイド検証はサーバーで実行されます。常にサーバーサイド検証を実装することが重要です。

その理由は、クライアントサイド検証は簡単にバイパスできるためです。悪意のあるユーザーはJavaScriptを無効にして、不正なデータをサーバーに送信する可能性があります。

このチュートリアルでは、クライアントサイド検証のみに焦点を当てます。

クライアントサイド検証オプション

クライアントサイド検証には、2つのオプションがあります。

  • JavaScript検証: JavaScriptを使用して検証ロジックを開発します。または、ライブラリを使用してこれを行うこともできます。
  • 組み込みフォーム検証: HTML5フォーム検証機能を使用できます。この検証は、JavaScript検証よりもパフォーマンスが優れています。ただし、JavaScript検証ほどカスタマイズできません。

JavaScript検証

ユーザー名、メールアドレス、パスワード、パスワード確認の4つの入力フィールドを持つシンプルなサインアップフォームを作成します。

何も入力せずに、または誤ったデータ形式を入力してサインアップをクリックすると、フォームにエラーメッセージが表示されます。

以下を検証します

  • ユーザー名は空白にすることができず、少なくとも3文字以上で、25文字以下である必要があります。
  • メールアドレスは必須であり、有効である必要があります。
  • パスワードは8文字以上である必要があります。また、1つの小文字、1つの大文字、1つの数字、およびこのセット(!@#$%^&*)に少なくとも1つの特殊文字を含める必要があります。
  • 確認パスワードはパスワードと同じである必要があります。

プロジェクト構造の作成

まず、プロジェクトのすべてのソースコードファイルを格納するform-validationフォルダを作成します。

次に、form-validationフォルダ内にjsフォルダとcssフォルダを作成します。

3番目に、cssフォルダにstyle.cssjsフォルダにapp.jsform-validationフォルダに直接index.htmlを作成します。

最終的なプロジェクト構造は次のようになります。

HTMLフォームの作成

まず、index.htmlファイルを開き、次のコードを入力します。

<!DOCTYPE html>
<html>
<head>
    <title>JavaScript Form Validation Demo</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>

    <script src="js/app.js"></script>
</body>

</html>Code language: HTML, XML (xml)

このHTMLファイルでは、style.cssファイルをheadセクションに、app.jsファイルを</body>終了タグの前にbodyセクションに配置します。

次に、次のHTMLマークアップを追加して、サインアップフォームを作成します。最終的なindex.htmlファイルは次のようになります。

 <!DOCTYPE html>
<html>
<head>
    <title>JavaScript Form Validation Demo</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div class="container">
        <form id="signup" class="form">
            <h1>Sign Up</h1>
            <div class="form-field">
                <label for="username">Username:</label>
                <input type="text" name="username" id="username" autocomplete="off">
                <small></small>
            </div>

            <div class="form-field">
                <label for="email">Email:</label>
                <input type="text" name="email" id="email" autocomplete="off">
                <small></small>
            </div>

            <div class="form-field">
                <label for="password">Password:</label>
                <input type="password" name="password" id="password" autocomplete="off">
                <small></small>
            </div>


            <div class="form-field">
                <label for="confirm-password">Confirm Password:</label>
                <input type="password" name="confirm-password" id="confirm-password" autocomplete="off">
                <small></small>
            </div>

            <div class="form-field">
                <input type="submit" value="Sign Up">
            </div>
        </form>
    </div>

    <script src="js/app.js"></script>
</body>
</html>Code language: HTML, XML (xml)

サインアップフォームの注目すべき点は、各フィールドがクラスform-fieldを持つdivで囲まれていることです。

各フォームフィールドには3つの要素があります

  • ラベル
  • 入力フィールド
  • <small>要素

<small>タグを使用して、ユーザーにエラーメッセージを表示します。

入力フィールドが無効な場合、form-field要素にerrorクラスを追加することで、境界線の色を赤にします。次のようになります。

<div class="form-field error">
   <label for="username">Username:</label>
   <input type="text" name="username" id="username" autocomplete="off">
   <small></small>
</div>Code language: JavaScript (javascript)

入力フィールドの値が有効な場合、form-field要素にsuccessクラスを追加することで、境界線の色を緑にします。次のようになります。

<div class="form-field success">
   <label for="username">Username:</label>
   <input type="text" name="username" id="username" autocomplete="off">
   <small></small>
</div>Code language: JavaScript (javascript)

.errorクラスと.successクラスの詳細は、style.cssを参照してください。

フォームフィールドを選択し、送信イベントリスナーを追加する

app.jsファイルでは、最初にdocument.querySelector()メソッドを使用して、入力フィールドとフォームを選択します。

const usernameEl = document.querySelector('#username');
const emailEl = document.querySelector('#email');
const passwordEl = document.querySelector('#password');
const confirmPasswordEl = document.querySelector('#confirm-password');

const form = document.querySelector('#signup');Code language: JavaScript (javascript)

次に、addEventListener()メソッドを使用して、submitイベントリスナーをフォームにアタッチします。

form.addEventListener('submit', function (e) {
    // prevent the form from submitting
    e.preventDefault();

});Code language: JavaScript (javascript)

イベントリスナーでは、送信ボタンがクリックされたらフォームの送信を防ぐために、e.preventDefault()を呼び出す必要があります。

ユーティリティ関数の開発

フォームを検証する前に、再利用可能なユーティリティ関数をいくつか開発して、以下を確認できます。

  • フィールドが必須かどうか。
  • フィールドの長さが最小値と最大値の間にあるかどうか。
  • メールアドレスの形式が有効かどうか。
  • パスワードが強力かどうか。

次のisRequired()関数は、入力引数が空の場合にtrueを返します。

const isRequired = value => value === '' ? false : true;Code language: JavaScript (javascript)

次のisBetween()関数は、length引数がmin引数とmax引数の間でない場合にfalseを返します。

const isBetween = (length, min, max) => length < min || length > max ? false : true;Code language: JavaScript (javascript)

メールアドレスが有効かどうかを確認するには、正規表現を使用します。

const isEmailValid = (email) => {
    const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(email);
};Code language: JavaScript (javascript)

指定されたパターンに一致する強力なパスワードかどうかを確認するには、正規表現も使用します。

const isPasswordSecure = (password) => {
    const re = new RegExp("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})");
    return re.test(password);
};Code language: JavaScript (javascript)

次の表は、パスワードの検証に使用される正規表現の各部分の意味を示しています。

パスワード正規表現意味
^パスワードの開始
(?=.*[a-z])パスワードには少なくとも1つの小文字が含まれている必要があります。
(?=.*[A-Z])パスワードには少なくとも1つの大文字が含まれている必要があります。
(?=.*[0-9])パスワードには少なくとも1つの数字が含まれている必要があります。
(?=.*[!@#$%^&*])パスワードには少なくとも1つの特殊文字が含まれている必要があります。
(?=.{8,})パスワードは8文字以上である必要があります。

エラー/成功を表示する関数の開発

次のshowError()関数は、入力フィールドの境界線を強調表示し、入力フィールドが無効な場合にエラーメッセージを表示します。

const showError = (input, message) => {
    // get the form-field element
    const formField = input.parentElement;
    // add the error class
    formField.classList.remove('success');
    formField.classList.add('error');

    // show the error message
    const error = formField.querySelector('small');
    error.textContent = message;
};Code language: JavaScript (javascript)

仕組み。

まず、入力フィールドの親要素を取得します。これは、form-fieldクラスを含む<div>要素です。

const formField = input.parentElement;Code language: JavaScript (javascript)

次に、successクラスを削除し、errorクラスをform-field要素に追加します。

formField.classList.remove('success');
formField.classList.add('error');Code language: JavaScript (javascript)

3番目に、form-field要素内の<small>要素を選択します。

const error = formField.querySelector('small');Code language: JavaScript (javascript)

document.querySelector()ではなく、formField.querySelector()を使用していることに注意してください。

最後に、エラーメッセージを<small>要素のtextContentプロパティに設定します。

error.textContent = message;

成功インディケータを表示する関数は、showError()関数に似ています。

const showSuccess = (input) => {
    // get the form-field element
    const formField = input.parentElement;

    // remove the error class
    formField.classList.remove('error');
    formField.classList.add('success');

    // hide the error message
    const error = formField.querySelector('small');
    error.textContent = '';
}Code language: JavaScript (javascript)

showError()関数とは異なり、showSuccess()関数はerrorクラスを削除し、successクラスを追加し、エラーメッセージを空白に設定します。

これで、上記のユーティリティ関数を使用して、各フィールドをチェックできます。

入力フィールド検証関数の開発

フォームフィールドの値を検証するための4つの関数を開発します。

1) ユーザー名フィールドの検証

次のcheckUsername()関数は、以下を使用します。

  • ユーザー名が入力されているかどうかを確認するためのisRequired()関数。
  • ユーザー名の長さが3文字から25文字の間にあるかどうかを確認するためのisBetween()関数。
  • エラーと成功のインディケータを表示するためのshowError()関数とshowSuccess()関数。

フィールドがチェックに合格した場合、関数はtrueを返します。

const checkUsername = () => {

    let valid = false;
    const min = 3,
        max = 25;
    const username = usernameEl.value.trim();

    if (!isRequired(username)) {
        showError(usernameEl, 'Username cannot be blank.');
    } else if (!isBetween(username.length, min, max)) {
        showError(usernameEl, `Username must be between ${min} and ${max} characters.`)
    } else {
        showSuccess(usernameEl);
        valid = true;
    }
    return valid;
}Code language: JavaScript (javascript)

2) メールアドレスフィールドの検証

checkEmail()関数は、メールアドレスが指定され、有効な場合にtrueを返します。

チェックには、isRequired()関数とisEmailValid()関数を使用します。また、エラーと成功の場合にフィードバックを提供するために、showError()関数とshowSuccess()関数を使用します。

const checkEmail = () => {
    let valid = false;
    const email = emailEl.value.trim();
    if (!isRequired(email)) {
        showError(emailEl, 'Email cannot be blank.');
    } else if (!isEmailValid(email)) {
        showError(emailEl, 'Email is not valid.')
    } else {
        showSuccess(emailEl);
        valid = true;
    }
    return valid;
}Code language: JavaScript (javascript)

3) パスワードフィールドの検証

次のcheckPassword()関数は、パスワードフィールドが指定され、必要な形式に一致するかどうかをチェックします。

const checkPassword = () => {

    let valid = false;

    const password = passwordEl.value.trim();

    if (!isRequired(password)) {
        showError(passwordEl, 'Password cannot be blank.');
    } else if (!isPasswordSecure(password)) {
        showError(passwordEl, 'Password must has at least 8 characters that include at least 1 lowercase character, 1 uppercase characters, 1 number, and 1 special character in (!@#$%^&*)');
    } else {
        showSuccess(passwordEl);
        valid = true;
    }

    return valid;
};Code language: JavaScript (javascript)

4) パスワード確認フィールドの検証

checkConfirmPassword()関数は、確認パスワードがパスワードと同じかどうかをチェックします。

const checkConfirmPassword = () => {
    let valid = false;
    // check confirm password
    const confirmPassword = confirmPasswordEl.value.trim();
    const password = passwordEl.value.trim();

    if (!isRequired(confirmPassword)) {
        showError(confirmPasswordEl, 'Please enter the password again');
    } else if (password !== confirmPassword) {
        showError(confirmPasswordEl, 'Confirm password does not match');
    } else {
        showSuccess(confirmPasswordEl);
        valid = true;
    }

    return valid;
};Code language: JavaScript (javascript)

送信イベントハンドラの変更

これで、送信イベントハンドラで入力フィールドを検証する関数を使用できます。

form.addEventListener('submit', function (e) {
    // prevent the form from submitting
    e.preventDefault();

    // validate forms
    let isUsernameValid = checkUsername(),
        isEmailValid = checkEmail(),
        isPasswordValid = checkPassword(),
        isConfirmPasswordValid = checkConfirmPassword();

    let isFormValid = isUsernameValid &&
        isEmailValid &&
        isPasswordValid &&
        isConfirmPasswordValid;

    // submit to the server if the form is valid
    if (isFormValid) {

    }
});Code language: JavaScript (javascript)

仕組み

  • まず、個々の関数を呼び出して、ユーザー名、メールアドレス、パスワード、パスワード確認フィールドを検証します。
  • 次に、&&演算子を使用して、フォームが有効かどうかを判断します。すべてのフィールドが有効な場合にのみ、フォームは有効です。
  • 最後に、isFormValidフラグで指定されたフォームが有効な場合、サーバーにデータを送信します。このチュートリアルでは、フォームデータをサーバーに送信する方法は扱いません。

これで、index.htmlファイルを開き、いくつかの値を入力して送信ボタンをクリックしてテストできます。

インスタントフィードバック機能の追加

フォームは、**サインアップ**ボタンをクリックしたときにのみエラーまたは成功を表示します.

インスタントフィードバックを提供するには、各フィールドのinputイベントにイベントリスナーをアタッチして検証します。

さらに、イベント делегированиеを使用して、inputイベントリスナーをフォームにアタッチし、現在のフィールドIDに基づいて各フィールドを検証することをお勧めします。次のようにします。

form.addEventListener('input', function (e) {
    switch (e.target.id) {
        case 'username':
            checkUsername();
            break;
        case 'email':
            checkEmail();
            break;
        case 'password':
            checkPassword();
            break;
        case 'confirm-password':
            checkConfirmPassword();
            break;
    }
});Code language: JavaScript (javascript)

index.htmlを開いてデータを入力すると、フォームにエラーまたは成功のフィードバックがすぐに表示されます。

また、デバウンス技術を使用してフォームのパフォーマンスを向上させます。

技術的には、入力を検証する前に、ユーザーが入力を短時間一時停止するか、入力を停止するまで待ちます。

デバウンス技術の詳細は、このチュートリアルで確認してください。

以下は、debounce()関数を示しています。

const debounce = (fn, delay = 500) => {
    let timeoutId;
    return (...args) => {
        // cancel the previous timer
        if (timeoutId) {
            clearTimeout(timeoutId);
        }
        // setup a new timer
        timeoutId = setTimeout(() => {
            fn.apply(null, args)
        }, delay);
    };
};Code language: JavaScript (javascript)

これで、inputイベントハンドラをdebounce()関数に渡して、デバウンスできます。

form.addEventListener('input', debounce(function (e) {
    switch (e.target.id) {
        case 'username':
            checkUsername();
            break;
        case 'email':
            checkEmail();
            break;
        case 'password':
            checkPassword();
            break;
        case 'confirm-password':
            checkConfirmPassword();
            break;
    }
}));Code language: JavaScript (javascript)

フォームフィールドにデータを入力してinputイベントをトリガーすると、エラーメッセージまたは成功メッセージに少し遅延があることがわかります。

以下は、完全なapp.jsファイルを示しています。

const usernameEl = document.querySelector('#username');
const emailEl = document.querySelector('#email');
const passwordEl = document.querySelector('#password');
const confirmPasswordEl = document.querySelector('#confirm-password');

const form = document.querySelector('#signup');


const checkUsername = () => {

    let valid = false;

    const min = 3,
        max = 25;

    const username = usernameEl.value.trim();

    if (!isRequired(username)) {
        showError(usernameEl, 'Username cannot be blank.');
    } else if (!isBetween(username.length, min, max)) {
        showError(usernameEl, `Username must be between ${min} and ${max} characters.`)
    } else {
        showSuccess(usernameEl);
        valid = true;
    }
    return valid;
};


const checkEmail = () => {
    let valid = false;
    const email = emailEl.value.trim();
    if (!isRequired(email)) {
        showError(emailEl, 'Email cannot be blank.');
    } else if (!isEmailValid(email)) {
        showError(emailEl, 'Email is not valid.')
    } else {
        showSuccess(emailEl);
        valid = true;
    }
    return valid;
};

const checkPassword = () => {
    let valid = false;


    const password = passwordEl.value.trim();

    if (!isRequired(password)) {
        showError(passwordEl, 'Password cannot be blank.');
    } else if (!isPasswordSecure(password)) {
        showError(passwordEl, 'Password must has at least 8 characters that include at least 1 lowercase character, 1 uppercase characters, 1 number, and 1 special character in (!@#$%^&*)');
    } else {
        showSuccess(passwordEl);
        valid = true;
    }

    return valid;
};

const checkConfirmPassword = () => {
    let valid = false;
    // check confirm password
    const confirmPassword = confirmPasswordEl.value.trim();
    const password = passwordEl.value.trim();

    if (!isRequired(confirmPassword)) {
        showError(confirmPasswordEl, 'Please enter the password again');
    } else if (password !== confirmPassword) {
        showError(confirmPasswordEl, 'The password does not match');
    } else {
        showSuccess(confirmPasswordEl);
        valid = true;
    }

    return valid;
};

const isEmailValid = (email) => {
    const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(email);
};

const isPasswordSecure = (password) => {
    const re = new RegExp("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})");
    return re.test(password);
};

const isRequired = value => value === '' ? false : true;
const isBetween = (length, min, max) => length < min || length > max ? false : true;


const showError = (input, message) => {
    // get the form-field element
    const formField = input.parentElement;
    // add the error class
    formField.classList.remove('success');
    formField.classList.add('error');

    // show the error message
    const error = formField.querySelector('small');
    error.textContent = message;
};

const showSuccess = (input) => {
    // get the form-field element
    const formField = input.parentElement;

    // remove the error class
    formField.classList.remove('error');
    formField.classList.add('success');

    // hide the error message
    const error = formField.querySelector('small');
    error.textContent = '';
}


form.addEventListener('submit', function (e) {
    // prevent the form from submitting
    e.preventDefault();

    // validate fields
    let isUsernameValid = checkUsername(),
        isEmailValid = checkEmail(),
        isPasswordValid = checkPassword(),
        isConfirmPasswordValid = checkConfirmPassword();

    let isFormValid = isUsernameValid &&
        isEmailValid &&
        isPasswordValid &&
        isConfirmPasswordValid;

    // submit to the server if the form is valid
    if (isFormValid) {

    }
});


const debounce = (fn, delay = 500) => {
    let timeoutId;
    return (...args) => {
        // cancel the previous timer
        if (timeoutId) {
            clearTimeout(timeoutId);
        }
        // setup a new timer
        timeoutId = setTimeout(() => {
            fn.apply(null, args)
        }, delay);
    };
};

form.addEventListener('input', debounce(function (e) {
    switch (e.target.id) {
        case 'username':
            checkUsername();
            break;
        case 'email':
            checkEmail();
            break;
        case 'password':
            checkPassword();
            break;
        case 'confirm-password':
            checkConfirmPassword();
            break;
    }
}));Code language: JavaScript (javascript)

そして、これが最終的なフォームです

まとめ

  • クライアントサイド検証とは何か、そしてクライアントサイド検証とサーバーサイド検証の違いについて説明しました。
  • フォームの作成方法と、JavaScriptとCSSを組み合わせて入力フィールドを検証する方法。
  • フィールド値が正しい形式かどうかを確認するために、正規表現を使用する方法。
  • イベント委任技術を使用する方法。
  • デバウンス技術を使用して、フォーム検証のパフォーマンスを向上させる方法。
このチュートリアルは役に立ちましたか?