JavaScript 表单验证

JavaScript 表单验证 (Form Validation) 深度指南

表单验证是 Web 开发中至关重要的一环。它不仅能提升用户体验(即时反馈),还能减轻服务器压力,但必须记住:前端验证永远不能替代后端验证(安全性)。

本指南将涵盖从 HTML5 原生验证到高级自定义验证的完整方案。


一、验证的两大阵营

1. HTML5 原生验证 (Constraint Validation API)

利用浏览器内置能力,无需写 JS 即可实现基础验证。

  • 优点:代码少、性能好、自动支持无障碍 (A11y)、自动显示浏览器默认提示。
  • 缺点:样式定制困难、逻辑复杂时难以维护、提示信息不够灵活。

2. JavaScript 自定义验证

完全由 JS 控制验证逻辑和 UI 反馈。

  • 优点:逻辑灵活、UI 完全可控、可集成复杂规则(如密码强度、实时比对)。
  • 缺点:代码量大、需处理浏览器兼容性、需手动处理无障碍。

最佳实践混合使用。利用 HTML5 处理基础规则(必填、格式),利用 JS 处理复杂逻辑和自定义 UI。


二、HTML5 原生验证 (Constraint Validation)

1. 常用属性

属性作用示例
required必填项<input required>
type类型检查email, url, number, tel
pattern正则匹配<input pattern="[A-Za-z]{3}">
min / max数值/日期范围<input type="number" min="1" max="100">
minlength / maxlength长度限制<input minlength="6">
step步长 (数字/日期)<input type="number" step="0.01">

2. 核心 API (Constraint Validation API)

属性/方法描述
element.validity返回 ValidityState 对象,包含详细状态
element.validationMessage浏览器默认的报错信息
element.checkValidity()返回 true (有效) 或 false (无效)
element.reportValidity()触发浏览器默认提示 UI (气泡)
element.setCustomValidity(msg)设置自定义错误 (非空字符串即视为无效)

3. ValidityState 对象详解

validity.validfalse 时,可以通过以下属性判断具体原因:

属性含义触发条件
valueMissing值缺失required 且为空
typeMismatch类型不匹配type="email" 但格式不对
tooShort / tooLong长度越界超出 minlength/maxlength
rangeUnderflow低于最小值< min
rangeOverflow高于最大值> max
patternMismatch正则不匹配pattern 不匹配
stepMismatch步长不匹配step 不匹配
badInput输入无效用户输入了类型不允许的值 (如数字框输入字母)
customError自定义错误调用了 setCustomValidity("msg")

4. 实战:原生验证 + 自定义提示

<form id="regForm">
  <div class="form-group">
    <label>邮箱:</label>
    <input type="email" id="email" required>
    <span class="error-msg"></span>
  </div>
  <div class="form-group">
    <label>密码:</label>
    <input type="password" id="password" minlength="8" required>
    <span class="error-msg"></span>
  </div>
  <button type="submit">注册</button>
</form>

<style>
  .form-group { margin-bottom: 15px; }
  .error-msg { color: red; font-size: 12px; display: block; min-height: 18px; }
  input:invalid:not(:placeholder-shown) { border-color: red; }
  input:valid:not(:placeholder-shown) { border-color: green; }
</style>

<script>
  const form = document.getElementById('regForm');
  const email = document.getElementById('email');
  const password = document.getElementById('password');

  // 监听 input 事件,实时验证
  [email, password].forEach(input => {
    input.addEventListener('input', () => {
      const errorSpan = input.nextElementSibling;
      
      if (input.validity.valid) {
        errorSpan.textContent = '';
        input.setCustomValidity(''); // 清除自定义错误
      } else {
        // 优先显示自定义错误,否则显示默认错误
        if (input.validity.customError) {
          errorSpan.textContent = input.validationMessage;
        } else if (input.validity.valueMissing) {
          errorSpan.textContent = '此项不能为空';
        } else if (input.validity.typeMismatch) {
          errorSpan.textContent = '请输入有效的邮箱地址';
        } else if (input.validity.tooShort) {
          errorSpan.textContent = `至少需要 ${input.minLength} 个字符`;
        } else {
          errorSpan.textContent = input.validationMessage; // 兜底
        }
      }
    });
  });

  // 提交处理
  form.addEventListener('submit', (e) => {
    e.preventDefault();
    
    // 触发所有验证并显示默认气泡 (可选)
    // form.reportValidity(); 

    if (form.checkValidity()) {
      alert('验证通过,提交数据!');
      // form.submit();
    } else {
      // 手动触发所有字段的错误提示
      form.reportValidity();
    }
  });
</script>

三、JavaScript 自定义验证 (高级场景)

当 HTML5 无法满足需求时(如:密码强度、两次密码比对、动态规则),使用 JS 完全控制。

1. 验证规则设计模式

建议将验证规则配置化,便于维护。

const rules = {
  email: {
    required: true,
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
    messages: {
      required: '邮箱不能为空',
      pattern: '邮箱格式不正确'
    }
  },
  password: {
    required: true,
    minLength: 8,
    pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/, // 必须包含大小写和数字
    messages: {
      required: '密码不能为空',
      minLength: '密码至少8位',
      pattern: '密码必须包含大小写字母和数字'
    }
  },
  confirmPassword: {
    required: true,
    equalTo: '#password', // 自定义规则:等于另一个字段
    messages: {
      required: '请确认密码',
      equalTo: '两次密码不一致'
    }
  }
};

2. 通用验证函数

function validateField(field, rules) {
  const value = field.value.trim();
  const errorSpan = field.nextElementSibling; // 假设错误信息在下一个元素

  // 1. 必填检查
  if (rules.required && !value) {
    showError(field, errorSpan, rules.messages.required);
    return false;
  }

  // 如果非必填且为空,直接通过
  if (!value) {
    clearError(field, errorSpan);
    return true;
  }

  // 2. 长度检查
  if (rules.minLength && value.length < rules.minLength) {
    showError(field, errorSpan, rules.messages.minLength);
    return false;
  }

  // 3. 正则检查
  if (rules.pattern && !rules.pattern.test(value)) {
    showError(field, errorSpan, rules.messages.pattern);
    return false;
  }

  // 4. 自定义规则 (如 equalTo)
  if (rules.equalTo) {
    const targetValue = document.querySelector(rules.equalTo).value;
    if (value !== targetValue) {
      showError(field, errorSpan, rules.messages.equalTo);
      return false;
    }
  }

  // 通过
  clearError(field, errorSpan);
  return true;
}

function showError(field, span, msg) {
  field.style.borderColor = 'red';
  span.textContent = msg;
  // 可选:移除 setCustomValidity 避免冲突
  field.setCustomValidity(msg); 
}

function clearError(field, span) {
  field.style.borderColor = '';
  span.textContent = '';
  field.setCustomValidity('');
}

3. 集成到表单

const form = document.getElementById('regForm');
const fields = {
  email: document.getElementById('email'),
  password: document.getElementById('password'),
  confirmPassword: document.getElementById('confirmPassword')
};

// 实时验证
Object.keys(fields).forEach(key => {
  const field = fields[key];
  field.addEventListener('blur', () => validateField(field, rules[key]));
  field.addEventListener('input', () => {
    // 输入时清除错误,提升体验
    if (field.style.borderColor === 'red') {
      validateField(field, rules[key]);
    }
  });
});

// 提交验证
form.addEventListener('submit', (e) => {
  e.preventDefault();
  let isValid = true;
  
  Object.keys(fields).forEach(key => {
    if (!validateField(fields[key], rules[key])) {
      isValid = false;
    }
  });

  if (isValid) {
    alert('提交成功!');
    // 发送 AJAX 请求
  }
});

四、常见验证场景代码片段

1. 密码强度验证 (实时反馈)

const password = document.getElementById('password');
const strengthBar = document.getElementById('strengthBar');

password.addEventListener('input', () => {
  const val = password.value;
  let strength = 0;
  
  if (val.length >= 8) strength++;
  if (/[a-z]/.test(val)) strength++;
  if (/[A-Z]/.test(val)) strength++;
  if (/\d/.test(val)) strength++;
  if (/[^a-zA-Z0-9]/.test(val)) strength++;

  // 更新 UI
  const colors = ['red', 'orange', 'yellow', 'lightgreen', 'green'];
  const labels = ['弱', '较弱', '中等', '较强', '强'];
  
  strengthBar.style.width = `${(strength / 5) * 100}%`;
  strengthBar.style.backgroundColor = colors[strength - 1] || 'red';
  strengthBar.textContent = labels[strength - 1] || '';
});

2. 手机号验证 (中国大陆)

function validatePhone(phone) {
  const regex = /^1[3-9]\d{9}$/;
  return regex.test(phone);
}

3. 身份证验证 (18位)

function validateIDCard(id) {
  const regex = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
  return regex.test(id);
}

五、用户体验 (UX) 最佳实践

  1. 实时反馈
    • blur (失去焦点) 时验证,而不是 submit 时。
    • input 时清除错误提示,不要让用户一直看到红字。
  2. 清晰的错误信息
    • 避免“输入错误”,要说“邮箱格式不正确,请包含 @ 符号”。
    • 错误信息颜色使用红色,位置在输入框下方。
  3. 不要过度验证
    • 用户还没输完不要报错(例如密码长度)。
    • 避免在输入过程中频繁弹出浏览器默认气泡。
  4. 无障碍 (A11y)
    • 使用 aria-invalid="true" 标记无效字段。
    • 使用 aria-describedby 关联错误信息,让屏幕阅读器读取。
    <input id="email" aria-invalid="true" aria-describedby="email-error">
    <span id="email-error" role="alert">邮箱格式错误</span>
    
  5. 防止重复提交
    • 提交时禁用按钮,防止用户多次点击。

六、安全警告:后端验证是必须的!

永远不要信任前端验证!

  • 用户可以禁用 JavaScript。
  • 用户可以修改 HTML 属性。
  • 用户可以直接通过 API 发送请求。

后端验证流程

  1. 接收数据。
  2. 再次执行所有验证规则(长度、格式、业务逻辑)。
  3. 如果验证失败,返回错误信息给前端。
  4. 如果验证通过,处理业务逻辑。

七、常用验证库 (可选)

如果项目复杂,手写验证逻辑太繁琐,可以使用成熟库:

  • VeeValidate (Vue 生态首选)
  • Formik + Yup (React 生态首选)
  • jQuery Validate (老旧项目)
  • Parsley.js (轻量级)

八、总结

场景推荐方案
基础验证 (必填、邮箱、数字)HTML5 required, type, pattern + reportValidity()
自定义错误提示setCustomValidity() + 自定义 UI
复杂逻辑 (密码强度、比对)JavaScript 自定义验证函数
实时反馈input / blur 事件监听
安全性必须在后端再次验证

核心公式

HTML5 基础规则 + JS 自定义逻辑 + 实时 UX 反馈 + 后端安全验证 = 完美的表单验证

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值