MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript Web编程中的表单验证

2024-08-182.2k 阅读

一、表单验证的重要性

在 Web 应用程序开发中,表单是用户与服务器交互的重要方式。用户通过表单输入各种信息,如注册信息、登录凭证、订单详情等。然而,用户输入的数据并不总是准确、完整或符合预期格式的。如果直接将这些未经验证的数据提交到服务器,可能会导致以下问题:

  1. 数据完整性问题:不完整的数据可能使数据库记录出现缺失字段,影响后续业务逻辑的正常运行。例如,在用户注册时,如果未填写邮箱地址,可能导致无法通过邮箱进行密码找回等操作。
  2. 数据格式错误:错误格式的数据可能无法被服务器正确解析和处理。比如,日期格式不符合要求,可能导致在日程安排或数据分析等功能中出现错误。
  3. 安全性风险:恶意用户可能通过表单输入恶意代码,进行 SQL 注入、跨站脚本攻击(XSS)等。例如,在输入框中输入一段恶意的 JavaScript 代码,如果没有进行验证和过滤,当页面显示该输入内容时,恶意代码就可能被执行,从而窃取用户信息或破坏网站。

因此,表单验证是确保 Web 应用程序正常运行、数据质量和安全性的关键环节。JavaScript 作为客户端脚本语言,在表单验证方面发挥着重要作用。它可以在用户提交表单前,即时检查输入数据的有效性,为用户提供及时反馈,减少不必要的服务器请求,提高用户体验。

二、JavaScript 表单验证基础

2.1 获取表单元素

在 JavaScript 中,要对表单进行验证,首先需要获取表单及其内部的元素。可以通过多种方式获取表单元素:

  1. 通过 document.getElementById:这是最常用的方法之一,通过元素的 id 属性获取对应的元素。例如,假设有一个表单的 id 为 “myForm”,可以这样获取:
var myForm = document.getElementById('myForm');
  1. 通过 document.formsdocument.forms 是一个包含页面所有表单的集合。可以通过索引或表单的 name 属性来访问特定的表单。例如:
// 通过索引访问第一个表单
var firstForm = document.forms[0];

// 通过 name 属性访问名为 “contactForm” 的表单
var contactForm = document.forms['contactForm'];
  1. 获取表单内的子元素:获取表单元素后,就可以进一步获取表单内的输入框、下拉框、复选框等子元素。同样可以使用 getElementById,也可以通过表单的 elements 属性,它是一个包含表单所有元素的集合。例如,对于表单内 id 为 “username” 的输入框:
var myForm = document.getElementById('myForm');
// 使用 getElementById
var usernameInput = document.getElementById('username');

// 使用 elements 属性
var usernameInput2 = myForm.elements['username'];

2.2 基本验证函数

  1. 检查必填字段:许多表单字段是必填的,例如用户名、密码等。可以编写一个函数来检查某个输入框是否为空。
function checkRequired(input) {
    return input.value.trim()!== '';
}

这里使用 trim() 方法去除字符串两端的空白字符,然后判断是否为空字符串。在实际使用时,可以这样调用:

<input type="text" id="username" />
<button type="button" onclick="validateUsername()">验证</button>
<script>
function validateUsername() {
    var usernameInput = document.getElementById('username');
    if (checkRequired(usernameInput)) {
        alert('用户名填写有效');
    } else {
        alert('用户名不能为空');
    }
}
</script>
  1. 检查输入长度:有时候需要限制输入内容的长度,比如密码长度要求在 6 - 12 位之间。
function checkLength(input, min, max) {
    var length = input.value.length;
    return length >= min && length <= max;
}

调用示例:

<input type="password" id="password" />
<button type="button" onclick="validatePassword()">验证</button>
<script>
function validatePassword() {
    var passwordInput = document.getElementById('password');
    if (checkLength(passwordInput, 6, 12)) {
        alert('密码长度有效');
    } else {
        alert('密码长度需在 6 到 12 位之间');
    }
}
</script>

三、常见表单字段的验证

3.1 文本输入框验证

  1. 电子邮件验证:电子邮件地址的格式有特定要求,一般遵循 “用户名@域名.顶级域名” 的格式。可以使用正则表达式来验证电子邮件地址。
function validateEmail(input) {
    var emailRegex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
    return emailRegex.test(input.value);
}

调用示例:

<input type="email" id="email" />
<button type="button" onclick="validateEmailInput()">验证</button>
<script>
function validateEmailInput() {
    var emailInput = document.getElementById('email');
    if (validateEmail(emailInput)) {
        alert('电子邮件地址有效');
    } else {
        alert('请输入有效的电子邮件地址');
    }
}
</script>
  1. 电话号码验证:电话号码的格式因地区而异,以下是一个简单的国内手机号码验证示例(11 位数字,以 1 开头)。
function validatePhone(input) {
    var phoneRegex = /^1\d{10}$/;
    return phoneRegex.test(input.value);
}

调用示例:

<input type="text" id="phone" />
<button type="button" onclick="validatePhoneInput()">验证</button>
<script>
function validatePhoneInput() {
    var phoneInput = document.getElementById('phone');
    if (validatePhone(phoneInput)) {
        alert('手机号码有效');
    } else {
        alert('请输入有效的手机号码');
    }
}
</script>

3.2 下拉框验证

下拉框通常用于提供一组预定义的选项。验证下拉框主要是检查用户是否选择了一个有效的选项。假设下拉框的 id 为 “country”,并且至少需要选择一个非默认的选项。

<select id="country">
    <option value="">请选择国家</option>
    <option value="China">中国</option>
    <option value="USA">美国</option>
</select>
<button type="button" onclick="validateCountry()">验证</button>
<script>
function validateCountry() {
    var countrySelect = document.getElementById('country');
    if (countrySelect.value!== '') {
        alert('国家选择有效');
    } else {
        alert('请选择一个国家');
    }
}
</script>

3.3 复选框验证

复选框用于让用户选择多个选项中的零个或多个。例如,在注册页面可能有 “我同意服务条款” 这样的复选框,必须勾选才能继续注册。

<input type="checkbox" id="terms" />我同意服务条款
<button type="button" onclick="validateTerms()">验证</button>
<script>
function validateTerms() {
    var termsCheckbox = document.getElementById('terms');
    if (termsCheckbox.checked) {
        alert('同意服务条款,可以继续');
    } else {
        alert('请勾选同意服务条款');
    }
}
</script>

3.4 单选框验证

单选框用于让用户从一组互斥的选项中选择一个。比如性别选择,只能选择 “男” 或 “女”。

<input type="radio" name="gender" value="male" />男
<input type="radio" name="gender" value="female" />女
<button type="button" onclick="validateGender()">验证</button>
<script>
function validateGender() {
    var genderRadios = document.querySelectorAll('input[name="gender"]');
    for (var i = 0; i < genderRadios.length; i++) {
        if (genderRadios[i].checked) {
            alert('性别选择有效');
            return;
        }
    }
    alert('请选择性别');
}
</script>

四、表单提交事件与验证流程

4.1 表单提交事件

在 HTML 中,表单有一个 submit 事件,当用户点击提交按钮(<input type="submit"><button type="submit">)时会触发该事件。可以通过 JavaScript 为表单的 submit 事件绑定一个处理函数,在提交表单前进行验证。

<form id="myForm">
    <input type="text" id="username" />
    <input type="submit" value="提交" />
</form>
<script>
var myForm = document.getElementById('myForm');
myForm.addEventListener('submit', function (event) {
    var usernameInput = document.getElementById('username');
    if (!checkRequired(usernameInput)) {
        alert('用户名不能为空');
        event.preventDefault(); // 阻止表单提交
    }
});
</script>

在上述代码中,通过 addEventListener 为表单的 submit 事件绑定了一个匿名函数。在函数内部,首先检查用户名输入框是否为空,如果为空则弹出提示并调用 event.preventDefault() 阻止表单提交。

4.2 完整验证流程

一个完整的表单验证流程通常包括对多个字段的验证。以下是一个用户注册表单的完整验证示例:

<form id="registerForm">
    <label for="username">用户名:</label>
    <input type="text" id="username" />
    <br />
    <label for="password">密码:</label>
    <input type="password" id="password" />
    <br />
    <label for="email">电子邮件:</label>
    <input type="email" id="email" />
    <br />
    <input type="submit" value="注册" />
</form>
<script>
function checkRequired(input) {
    return input.value.trim()!== '';
}

function checkLength(input, min, max) {
    var length = input.value.length;
    return length >= min && length <= max;
}

function validateEmail(input) {
    var emailRegex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
    return emailRegex.test(input.value);
}

var registerForm = document.getElementById('registerForm');
registerForm.addEventListener('submit', function (event) {
    var usernameInput = document.getElementById('username');
    var passwordInput = document.getElementById('password');
    var emailInput = document.getElementById('email');

    if (!checkRequired(usernameInput)) {
        alert('用户名不能为空');
        event.preventDefault();
        return;
    }

    if (!checkLength(passwordInput, 6, 12)) {
        alert('密码长度需在 6 到 12 位之间');
        event.preventDefault();
        return;
    }

    if (!validateEmail(emailInput)) {
        alert('请输入有效的电子邮件地址');
        event.preventDefault();
        return;
    }

    alert('所有字段验证通过,表单提交');
});
</script>

在这个示例中,当用户点击注册按钮时,会依次检查用户名是否必填、密码长度是否符合要求以及电子邮件地址是否有效。只要有一个验证不通过,就会弹出相应提示并阻止表单提交。只有当所有验证都通过时,才会提示验证通过并允许表单提交。

五、自定义验证提示与样式

5.1 自定义验证提示

默认情况下,当验证失败时,我们通过 alert() 弹出简单的提示信息。然而,这种方式在用户体验上可能不够友好,并且不够灵活。可以通过创建自定义的提示元素来提供更丰富的验证反馈。

<form id="loginForm">
    <label for="username">用户名:</label>
    <input type="text" id="username" />
    <span id="usernameError" class="error"></span>
    <br />
    <input type="submit" value="登录" />
</form>
<script>
function checkRequired(input, errorElement) {
    if (input.value.trim() === '') {
        errorElement.textContent = '用户名不能为空';
        return false;
    } else {
        errorElement.textContent = '';
        return true;
    }
}

var loginForm = document.getElementById('loginForm');
loginForm.addEventListener('submit', function (event) {
    var usernameInput = document.getElementById('username');
    var usernameError = document.getElementById('usernameError');

    if (!checkRequired(usernameInput, usernameError)) {
        event.preventDefault();
    }
});
</script>

在上述代码中,我们在 HTML 中为用户名输入框创建了一个 span 元素用于显示错误提示。checkRequired 函数不仅检查输入是否为空,还会根据结果设置错误提示元素的文本内容。这样可以在输入框旁边实时显示友好的错误提示,而不是通过弹窗。

5.2 验证样式

除了自定义提示信息,还可以通过 CSS 样式来突出显示验证状态。例如,当输入框验证失败时,给它添加一个红色边框,验证成功时添加一个绿色边框。

<style>
.error {
    color: red;
}

.valid {
    border: 1px solid green;
}

.invalid {
    border: 1px solid red;
}
</style>
<form id="contactForm">
    <label for="email">电子邮件:</label>
    <input type="email" id="email" />
    <span id="emailError" class="error"></span>
    <br />
    <input type="submit" value="提交" />
</form>
<script>
function validateEmail(input, errorElement) {
    var emailRegex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
    if (emailRegex.test(input.value)) {
        input.classList.add('valid');
        input.classList.remove('invalid');
        errorElement.textContent = '';
        return true;
    } else {
        input.classList.add('invalid');
        input.classList.remove('valid');
        errorElement.textContent = '请输入有效的电子邮件地址';
        return false;
    }
}

var contactForm = document.getElementById('contactForm');
contactForm.addEventListener('submit', function (event) {
    var emailInput = document.getElementById('email');
    var emailError = document.getElementById('emailError');

    if (!validateEmail(emailInput, emailError)) {
        event.preventDefault();
    }
});
</script>

在这个示例中,通过 CSS 定义了 .valid.invalid 两个类,分别用于表示验证成功和失败的样式。在 validateEmail 函数中,根据验证结果为输入框添加或移除相应的类,从而实现样式的动态变化,直观地向用户展示验证状态。

六、复杂表单验证场景

6.1 依赖字段验证

有时候,一个字段的验证规则取决于另一个字段的值。例如,在一个订单表单中,如果用户选择了 “货到付款” 支付方式,那么 “信用卡信息” 字段就不应该填写;如果选择了 “信用卡支付”,则必须填写 “信用卡信息”。

<form id="orderForm">
    <label for="paymentMethod">支付方式:</label>
    <select id="paymentMethod">
        <option value="cod">货到付款</option>
        <option value="creditCard">信用卡支付</option>
    </select>
    <br />
    <label for="creditCardInfo">信用卡信息:</label>
    <input type="text" id="creditCardInfo" />
    <span id="creditCardError" class="error"></span>
    <br />
    <input type="submit" value="提交订单" />
</form>
<script>
function checkCreditCardInfo(input, errorElement, paymentMethod) {
    if (paymentMethod.value === 'creditCard') {
        if (input.value.trim() === '') {
            errorElement.textContent = '选择信用卡支付时,信用卡信息必填';
            return false;
        } else {
            errorElement.textContent = '';
            return true;
        }
    } else {
        errorElement.textContent = '';
        return true;
    }
}

var orderForm = document.getElementById('orderForm');
orderForm.addEventListener('submit', function (event) {
    var paymentMethodSelect = document.getElementById('paymentMethod');
    var creditCardInput = document.getElementById('creditCardInfo');
    var creditCardError = document.getElementById('creditCardError');

    if (!checkCreditCardInfo(creditCardInput, creditCardError, paymentMethodSelect)) {
        event.preventDefault();
    }
});
</script>

在上述代码中,checkCreditCardInfo 函数根据支付方式选择来决定是否验证信用卡信息字段。当选择信用卡支付时,检查信用卡信息是否为空,否则不进行验证。

6.2 动态表单验证

有些表单可能会根据用户的操作动态添加或移除字段。例如,在一个调查问卷表单中,用户点击 “添加问题” 按钮会动态添加一个新的问题输入框。对于动态生成的表单字段,同样需要进行验证。

<form id="surveyForm">
    <div id="questionContainer">
        <div class="question">
            <label for="question1">问题 1:</label>
            <input type="text" id="question1" />
        </div>
    </div>
    <button type="button" onclick="addQuestion()">添加问题</button>
    <input type="submit" value="提交问卷" />
</form>
<script>
function checkRequired(input) {
    return input.value.trim()!== '';
}

function addQuestion() {
    var questionContainer = document.getElementById('questionContainer');
    var newQuestionDiv = document.createElement('div');
    newQuestionDiv.classList.add('question');
    var questionNumber = questionContainer.childNodes.length + 1;
    var label = document.createElement('label');
    label.setAttribute('for', 'question' + questionNumber);
    label.textContent = '问题'+ questionNumber + ':';
    var input = document.createElement('input');
    input.setAttribute('type', 'text');
    input.setAttribute('id', 'question' + questionNumber);
    newQuestionDiv.appendChild(label);
    newQuestionDiv.appendChild(input);
    questionContainer.appendChild(newQuestionDiv);
}

var surveyForm = document.getElementById('surveyForm');
surveyForm.addEventListener('submit', function (event) {
    var questions = document.querySelectorAll('.question input');
    for (var i = 0; i < questions.length; i++) {
        if (!checkRequired(questions[i])) {
            alert('问题'+ (i + 1) +'不能为空');
            event.preventDefault();
            return;
        }
    }
});
</script>

在这个示例中,addQuestion 函数用于动态添加新的问题输入框。在表单提交时,通过 querySelectorAll 获取所有问题输入框,并依次检查它们是否为空。这样就实现了对动态表单字段的验证。

七、与服务器端验证的结合

虽然 JavaScript 在客户端进行表单验证可以提供即时反馈和良好的用户体验,但它并不能完全替代服务器端验证。原因如下:

  1. 安全性:客户端验证可以被绕过,恶意用户可以通过禁用 JavaScript 或者直接修改 HTML 页面来提交未经验证的数据。服务器端验证是防止恶意数据进入系统的最后一道防线。
  2. 数据一致性:服务器端验证可以确保数据在存储到数据库或进行进一步处理之前符合业务规则。例如,在用户注册时,服务器端可以验证用户名是否已被注册,这需要查询数据库,而客户端无法直接进行这样的操作。

通常的做法是在客户端使用 JavaScript 进行初步验证,在用户提交表单后,服务器端再次对数据进行验证。以用户注册为例,假设服务器使用 Node.js 和 Express 框架:

7.1 客户端代码

<form id="registerForm" action="/register" method="post">
    <label for="username">用户名:</label>
    <input type="text" id="username" />
    <br />
    <label for="password">密码:</label>
    <input type="password" id="password" />
    <br />
    <input type="submit" value="注册" />
</form>
<script>
function checkRequired(input) {
    return input.value.trim()!== '';
}

var registerForm = document.getElementById('registerForm');
registerForm.addEventListener('submit', function (event) {
    var usernameInput = document.getElementById('username');
    var passwordInput = document.getElementById('password');

    if (!checkRequired(usernameInput)) {
        alert('用户名不能为空');
        event.preventDefault();
        return;
    }

    if (!checkRequired(passwordInput)) {
        alert('密码不能为空');
        event.preventDefault();
        return;
    }
});
</script>

7.2 服务器端代码(Node.js + Express)

const express = require('express');
const app = express();
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: true }));

app.post('/register', function (req, res) {
    var username = req.body.username;
    var password = req.body.password;

    // 服务器端验证
    if (!username || username.trim() === '') {
        return res.status(400).send('用户名不能为空');
    }

    if (!password || password.trim() === '') {
        return res.status(400).send('密码不能为空');
    }

    // 假设这里进行数据库插入操作
    //...

    res.send('注册成功');
});

const port = 3000;
app.listen(port, function () {
    console.log('服务器在端口'+ port +'运行');
});

在这个示例中,客户端使用 JavaScript 进行初步验证,确保用户名和密码不为空。服务器端接收到表单数据后,再次进行相同的验证,并且可以进行更复杂的业务逻辑验证,如检查用户名是否唯一等。这样通过客户端和服务器端验证的结合,既提供了良好的用户体验,又保证了数据的安全性和一致性。