Javascript 表单验证

摘要: 在本教程中,您将通过从头开始构建注册表单来学习 JavaScript 表单验证。

什么是表单验证

将数据提交到服务器之前,您应该在网页浏览器中检查数据,以确保提交的数据格式正确。

为了提供快速反馈,您可以使用 JavaScript 验证数据。这称为客户端验证。

如果您不进行客户端验证,可能会导致用户体验不佳。在这种情况下,您可能会感到明显的延迟,因为表单数据在网页浏览器和服务器之间传输需要时间。

与在网页浏览器中执行的客户端验证不同,服务器端验证在服务器上执行。始终实施服务器端验证至关重要。

原因是客户端验证很容易绕过。恶意用户可以禁用 JavaScript 并将错误数据提交到您的服务器。

在本教程中,您将只关注客户端验证。

客户端验证选项

在客户端验证方面,您有两种选择

  • JavaScript 验证:您可以使用 JavaScript 开发验证逻辑。或者,您可以使用库来完成此操作。
  • 内置表单验证:您可以使用 HTML5 表单验证功能。这种验证比 JavaScript 验证性能更好。但是,它不像 JavaScript 验证那样可定制。

JavaScript 验证

您将创建一个包含四个输入字段的简单注册表单:用户名、电子邮件、密码和确认密码。

当您单击注册而不填写任何内容或填写不正确的格式的数据时,表单将显示错误消息

您将验证以下内容

  • 用户名不能为空,至少 3 个字符,不能超过 25 个字符。
  • 电子邮件是强制性的且有效的。
  • 密码至少 8 个字符。并且它必须包含 1 个小写字符、1 个大写字符、1 个数字以及至少一个此集合中的特殊字符 (!@#$%^&*)。
  • 确认密码必须与密码相同。

创建项目结构

首先,创建一个 form-validation 文件夹,用于存储项目的所有源代码文件。

其次,在 form-validation 文件夹内创建 jscss 文件夹。

第三,在 css 文件夹中创建 style.css,在 js 文件夹中创建 app.js,并在 form-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 包装起来。

每个表单字段包含三个元素

  • 一个标签
  • 一个输入字段
  • 一个 <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)

查看style.css,以了解 .error.success 类的详细信息。

选择表单字段并添加提交事件监听器

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 参数不在 minmax 参数之间时返回 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)

下表说明了用于验证密码的正则表达式的每个部分的含义

密码 RegEx含义
^密码开头
(?=.*[a-z])密码必须包含至少一个小写字符
(?=.*[A-Z])密码必须包含至少一个大写字符
(?=.*[0-9])密码必须包含至少一个数字
(?=.*[!@#$%^&*])密码必须包含至少一个特殊字符。
(?=.{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)

其次,从 form-field 元素中删除 success 类并添加 error

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

第三,选择 form-field 元素内部的 <small> 元素

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

请注意,您使用 formField.querySelector() 而不是 document.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 类,并将错误消息设置为空白。

现在,您可以使用上面的实用函数来检查每个字段。

开发输入字段验证函数

您将开发四个函数来验证表单字段的值

1) 验证用户名字段

以下 checkUsername() 函数使用

  • isRequired() 函数来检查是否提供了用户名。
  • isBetween() 函数来检查用户名的长度是否介于 3 到 25 个字符之间。
  • 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 结合起来验证输入字段。
  • 如何使用 正则表达式 检查字段值是否格式正确。
  • 如何使用 事件委托 技术。
  • 如何使用 去抖动技术 来提高表单验证的性能。
本教程对您有帮助吗?