← JavaScript JSON.stringify() 没有下一篇了 →

JavaScript表单验证

原创 2026-03-15 JavaScript 已有人查阅

JavaScript表单验证指南:从入门到项目实战

为什么要做表单验证?

2019年我做了一个用户反馈系统,上线第三天数据库里就多了条奇怪记录:用户名字段存的是"<script>alert('xss')</script>"。虽然当时没造成实际损失,但这个教训让我记住了——不要相信用户输入的数据。

表单验证的核心作用就两个:一是保证数据格式正确,二是防止恶意数据提交。虽然服务器端验证必不可少,但客户端验证能提供即时反馈,用户体验更好,同时减轻服务器压力。

基础表单验证实现

最简单的非空验证

function validateForm() {
  // 获取表单字段值
  const username = document.forms["registerForm"]["username"].value;
  const password = document.forms["registerForm"]["password"].value;
  
  // 用户名不能为空
  if (username === null || username === "") {
    alert("用户名不能为空");
    return false;
  }
  
  // 密码长度至少6位
  if (password.length < 6) {
    alert("密码长度不能少于6个字符");
    return false;
  }
  
  return true; // 所有验证通过
}
 
<!-- 表单结构 -->
<form name="registerForm" method="post" action="/register" onsubmit="return validateForm()">
  <div>
    <label>用户名:</label>
    <input type="text" name="username">
  </div>
  <div>
    <label>密码:</label>
    <input type="password" name="password">
  </div>
  <button type="submit">注册</button>
</form>

关键点onsubmit="return validateForm()" 这里的 return 不能少,少了就无法阻止表单提交。

密码一致性验证

用户注册时最常见的需求——确认密码:

function checkPasswordMatch() {
  const password = document.forms["registerForm"]["password"].value;
  const confirmPassword = document.forms["registerForm"]["confirmPassword"].value;
  
  if (password !== confirmPassword) {
    alert("两次输入的密码不一致");
    return false;
  }
  
  return true;
}

我见过不少网站只在后端验证密码一致性,用户体验很差——填完一堆信息点提交才被告知密码不一致。用JavaScript在前端即时提示,明显更友好。

字段级实时验证

弹窗提示虽然简单,但体验不够好。更专业的方式是在字段旁边显示提示信息:

function validateField(fieldName, value) {
  let isValid = true;
  let message = "";
  
  switch(fieldName) {
    case "username":
      if (value.length < 2) {
        isValid = false;
        message = "用户名至少2个字符";
      } else if (value.length > 20) {
        isValid = false;
        message = "用户名不能超过20个字符";
      }
      break;
      
    case "password":
      if (value.length < 6) {
        isValid = false;
        message = "密码至少6位";
      } else if (!/[A-Z]/.test(value)) {
        isValid = false;
        message = "密码需包含至少一个大写字母";
      } else if (!/[0-9]/.test(value)) {
        isValid = false;
        message = "密码需包含至少一个数字";
      }
      break;
      
    case "email":
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(value)) {
        isValid = false;
        message = "请输入有效的邮箱地址";
      }
      break;
  }
  
  // 更新UI显示
  const messageElement = document.getElementById(fieldName + "Error");
  const inputElement = document.getElementById(fieldName);
  
  if (messageElement) {
    messageElement.textContent = message;
    messageElement.style.color = isValid  "green" : "red";
  }
  
  if (inputElement) {
    inputElement.style.borderColor = isValid  "#4CAF50" : "#f44336";
  }
  
  return isValid;
}
 
<form name="registerForm" id="registerForm">
  <div>
    <label>用户名:</label>
    <input type="text" id="username" name="username" 
           oninput="validateField('username', this.value)">
    <span id="usernameError" class="error-message"></span>
  </div>
  
  <div>
    <label>邮箱:</label>
    <input type="email" id="email" name="email"
           oninput="validateField('email', this.value)">
    <span id="emailError" class="error-message"></span>
  </div>
  
  <div>
    <label>密码:</label>
    <input type="password" id="password" name="password"
           oninput="validateField('password', this.value)">
    <span id="passwordError" class="error-message"></span>
  </div>
</form>

个人经验oninput 事件比 onchange 或 onblur 体验更好,用户在输入时就能得到即时反馈。但要注意频繁触发带来的性能问题,复杂的验证逻辑可以考虑使用防抖函数。

数字验证实战

数字验证最常见的坑是用户输入非数字字符。isNaN() 函数是判断数字的利器:

function validateNumber() {
  const ageInput = document.getElementById("age");
  const age = ageInput.value;
  const errorElement = document.getElementById("ageError");
  
  // 检查是否为空
  if (age === "") {
    errorElement.textContent = "年龄不能为空";
    return false;
  }
  
  // 检查是否为数字
  if (isNaN(age)) {
    errorElement.textContent = "请输入有效的数字";
    return false;
  }
  
  // 检查范围
  const ageNum = Number(age);
  if (ageNum < 1 || ageNum > 120) {
    errorElement.textContent = "年龄必须在1-120之间";
    return false;
  }
  
  // 检查是否为整数
  if (!Number.isInteger(ageNum)) {
    errorElement.textContent = "年龄必须是整数";
    return false;
  }
  
  errorElement.textContent = "✓";
  return true;
}
 
<div>
  <label>年龄:</label>
  <input type="text" id="age" oninput="validateNumber()">
  <span id="ageError"></span>
</div>

进阶技巧isNaN() 会把空字符串、空格等视为数字,所以要先检查空值。更严格的做法是用正则:/^\d+$/ 确保只包含数字。

邮箱验证的精髓

很多初学者用复杂的正则验证邮箱,实没必要那么复杂。基本的邮箱格式验证就够了:

function validateEmail(email) {
  // 移除前后空格
  email = email.trim();
  
  // 基础检查
  if (email === "") {
    return { isValid: false, message: "邮箱不能为空" };
  }
  
  // 必须包含@
  const atIndex = email.indexOf("@");
  if (atIndex < 1) {
    return { isValid: false, message: "邮箱必须包含@,且@前至少有一个字符" };
  }
  
  // @后必须有.,且不能挨着
  const dotIndex = email.lastIndexOf(".");
  if (dotIndex < atIndex + 2) {
    return { isValid: false, message: "@后必须包含.,且中间至少有一个字符" };
  }
  
  // .后至少有两个字符
  if (dotIndex + 2 >= email.length) {
    return { isValid: false, message: "域名后缀至少两个字符" };
  }
  
  // 不能包含空格和中文(简单检查)
  if (/[\u4e00-\u9fa5\s]/.test(email)) {
    return { isValid: false, message: "邮箱不能包含空格或中文" };
  }
  
  return { isValid: true, message: "邮箱格式正确" };
}

// 使用示例
function checkEmailInput() {
  const emailInput = document.getElementById("email");
  const result = validateEmail(emailInput.value);
  
  const errorElement = document.getElementById("emailError");
  errorElement.textContent = result.message;
  errorElement.style.color = result.isValid  "green" : "red";
  emailInput.style.borderColor = result.isValid  "#4CAF50" : "#f44336";
  
  return result.isValid;
}

为什么不用太复杂的正则?因为邮箱标准实允许很多特殊字符,过度验证把合法邮箱拒之门外。我见过有人用超长正则,结果连 user+tag@gmail.com 这种标准格式都验证失败。

综合示例:完整的注册表单验证

把上面所有技术整合起来,做一个完整的注册表单:

// 全功能表单验证
function validateFullForm() {
  let isValid = true;
  
  // 验证用户名
  const username = document.getElementById("username").value;
  if (username.length < 2) {
    showError("username", "用户名至少2个字符");
    isValid = false;
  } else {
    clearError("username");
  }
  
  // 验证邮箱
  const email = document.getElementById("email").value;
  const emailResult = validateEmail(email);
  if (!emailResult.isValid) {
    showError("email", emailResult.message);
    isValid = false;
  } else {
    clearError("email");
  }
  
  // 验证密码
  const password = document.getElementById("password").value;
  if (password.length < 6) {
    showError("password", "密码至少6位");
    isValid = false;
  } else if (!/[A-Z]/.test(password)) {
    showError("password", "密码需包含大写字母");
    isValid = false;
  } else if (!/[0-9]/.test(password)) {
    showError("password", "密码需包含数字");
    isValid = false;
  } else {
    clearError("password");
  }
  
  // 验证确认密码
  const confirmPass = document.getElementById("confirmPassword").value;
  if (confirmPass !== password) {
    showError("confirmPassword", "两次密码不一致");
    isValid = false;
  } else {
    clearError("confirmPassword");
  }
  
  // 验证年龄
  const age = document.getElementById("age").value;
  if (!validateAge(age)) {
    showError("age", "请输入有效年龄(1-120)");
    isValid = false;
  } else {
    clearError("age");
  }
  
  // 显示整体结果
  if (isValid) {
    alert("表单验证通过,可以提交");
    // 这里可以执行真正的表单提交
    // document.getElementById("registerForm").submit();
  }
  
  return isValid;
}

function showError(fieldId, message) {
  const errorElement = document.getElementById(fieldId + "Error");
  const inputElement = document.getElementById(fieldId);
  if (errorElement) {
    errorElement.textContent = message;
    errorElement.className = "error-message visible";
  }
  if (inputElement) {
    inputElement.className = "input-error";
  }
}

function clearError(fieldId) {
  const errorElement = document.getElementById(fieldId + "Error");
  const inputElement = document.getElementById(fieldId);
  if (errorElement) {
    errorElement.textContent = "";
    errorElement.className = "error-message";
  }
  if (inputElement) {
    inputElement.className = "";
  }
}

function validateAge(age) {
  if (age === "") return false;
  if (isNaN(age)) return false;
  const ageNum = Number(age);
  if (ageNum < 1 || ageNum > 120) return false;
  if (!Number.isInteger(ageNum)) return false;
  return true;
}
 
<style>
  .error-message {
    color: #f44336;
    font-size: 12px;
    margin-left: 10px;
    display: none;
  }
  .error-message.visible {
    display: inline;
  }
  .input-error {
    border: 1px solid #f44336;
  }
  .form-group {
    margin-bottom: 15px;
  }
  label {
    display: inline-block;
    width: 100px;
  }
  button {
    margin-left: 100px;
    padding: 8px 20px;
  }
</style>

<form id="registerForm" onsubmit="return false">
  <div class="form-group">
    <label>用户名:</label>
    <input type="text" id="username">
    <span id="usernameError" class="error-message"></span>
  </div>
  
  <div class="form-group">
    <label>邮箱:</label>
    <input type="text" id="email">
    <span id="emailError" class="error-message"></span>
  </div>
  
  <div class="form-group">
    <label>密码:</label>
    <input type="password" id="password">
    <span id="passwordError" class="error-message"></span>
  </div>
  
  <div class="form-group">
    <label>确认密码:</label>
    <input type="password" id="confirmPassword">
    <span id="confirmPasswordError" class="error-message"></span>
  </div>
  
  <div class="form-group">
    <label>年龄:</label>
    <input type="text" id="age">
    <span id="ageError" class="error-message"></span>
  </div>
  
  <button onclick="validateFullForm()">注册</button>
</form>

课程知识要点

  • 表单验证的必要性:防止无效数据提交,提升用户体验,减轻服务器负担

  • onsubmit事件:在表单提交前拦截验证,return false可阻止提交

  • 实时验证:使用oninput事件在输入时即时反馈

  • 常见验证规则:非空检查、长度限制、格式验证、范围验证

  • 密码验证:长度、复杂度、一致性检查

  • 数字验证:使用isNaN()判断是否为数字,结合范围检查

  • 邮箱验证:检查@和.的位置,不需要过度复杂的正则

  • 错误提示:友好的错误信息展示,边框颜色变化增强可视性

  • 验证时机选择:oninput(即时)、onblur(离开焦点)、onsubmit(提交前)

  • 客户端验证局限性:只能提升体验,不能替代服务器端验证

为什么不用纯后端验证?

有人会问:既然服务器端验证是必须的,为什么还要做前端验证?

举个实际场景:用户填了个10页的表单,点提交后等了5秒,页面刷新提示"邮箱格式错误"。这时候用户的耐心已经被消耗殆尽。

前端验证的价值在于即时反馈——用户填完邮箱就能立刻知道格式是否正确,填完密码就能看到复杂度是否达标。这不是替代后端验证,而是两者配合,各司职。前端负责体验,后端负责安全。

掌握了这些表单验证技巧,你就能做出用户填起来很顺畅、数据质量又高的表单。这是每个前端开发者必须掌握的基础能力。

← JavaScript JSON.stringify() 没有下一篇了 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号