HTML 表单验证怎么做:4 种方式一次讲清含原生属性与 JS 实战

编程狮(w3cschool.cn) 2026-09-22 15:36:27 浏览数 (28)
反馈

做 HTML 表单验证,最省事的是用 required、pattern 等原生属性拦截空值和格式,再用 JS 监听 submit 做复杂校验。本文讲清 4 种方式及只靠前端的局限。很多同学写登录注册页时,习惯把校验全写在后端,结果用户填错要等请求回来才知道,体验很差。其实浏览器原生就提供了一套校验机制,配合少量 JavaScript 就能在提交前拦住大部分错误。我们要先分清两类手段:一类是 HTML5 原生属性,零脚本即可生效;另一类是用 JavaScript 接管提交事件,做跨字段、异步等原生做不到的逻辑。下面从最简单的必填开始,逐层叠加,最后提醒你纯前端校验永远可以被绕过,不能替代服务端校验。

HTML 表单验证4 种方式实战

一、用 required 做原生必填校验

在 input、textarea、select 上加 required 属性,浏览器会在表单提交时自动检查该字段是否为空,为空则阻止提交并弹出提示。这是零成本的入门方式,不需要写任何脚本。但要注意,required 只判断“有没有值”,不判断格式对不对,也不能跨字段比较。在 HTML 标签参考 里可以看到,required 是布尔属性,出现在标签上即生效。下面是一段可运行的登录表单,用户名和密码都设为必填:

<form>
  <p>用户名:<input name="user" required></p>
  <p>密码:<input name="pwd" type="password" required></p>
  <button type="submit">提交</button>
</form>

直接保存为 html 打开,留空点击提交,浏览器会高亮空字段并提示“请填写此字段”,整个过程无需后端参与。

若你在调试时希望临时关掉原生校验去手动测试 JS 逻辑,可给 form 加 novalidate 属性,或用按钮上的 formnovalidate 局部跳过。调试完记得移除,否则用户就失去了原生保护,空数据可能直接发往后端。

二、用 pattern 做正则格式校验

当字段需要满足特定格式,比如手机号、邮箱、邮编时,用 pattern 属性写上正则表达式,浏览器会按规则校验。pattern 的值是一个不含斜杠的 JS 正则主体,例如手机号可写 pattern="1[3-9]\d{9}"。它和 required 可以叠加:字段为空时 required 先报空,有值但不匹配时 pattern 报错。下面演示邮箱与手机号的组合校验:

<form>
  <p>手机:<input name="tel" pattern="1[3-9]\d{9}" required></p>
  <p>邮箱:<input name="mail" type="email" required></p>
  <button type="submit">提交</button>
</form>

注意 pattern 默认区分大小写,需要忽略大小写时要在正则里加 (?i) 或换写法;另外部分浏览器对中文提示支持有限,建议配合 title 属性给出格式说明。

实际写 pattern 时,建议把人类可读的格式要求写进 title,部分浏览器会把它作为错误提示的一部分展示给用户。多个规则可拆成多个 input 分别校验,避免一条超长正则难以维护,也方便针对单字段给出更精准的错误信息。

三、用 JavaScript 监听 submit 做自定义校验

原生属性搞不定的复杂逻辑,比如“两次密码一致”“用户名长度在 6 到 18 位”,要用 JavaScript 监听表单的 submit 事件,在回调里判断并调用 preventDefault 阻止提交。在 JavaScript 教程 中,事件与 DOM 操作有系统讲解,建议先打基础。下面演示确认密码一致的实战,错误时在页面上显示中文提示而不是默认的英文气泡:

<form id="f">
  <p>密码:<input id="p1" type="password"></p>
  <p>确认:<input id="p2" type="password"></p>
  <p id="msg" style="color:red"></p>
  <button type="submit">提交</button>
</form>
<script>
  document.getElementById('f').addEventListener('submit', function(e){
    if (document.getElementById('p1').value !== document.getElementById('p2').value) {
      e.preventDefault();
      document.getElementById('msg').textContent = '两次密码不一致';
    }
  });
</script>

这段代码在两端不一致时拦截提交,并给出可读的中文反馈,体验比原生提示更可控。

除了监听 submit,也能在任意时刻调用 form.checkValidity() 主动判断整张表单是否合法,它返回布尔值;reportValidity() 则会同时弹出浏览器自带的提示气泡。两者适合在输入框失焦或按钮点击时做即时反馈,让用户在离开字段时就知道填错了。

四、用 setCustomValidity 自定义错误提示

setCustomValidity 是 HTML5 约束校验 API 的一部分,能在原生校验流程里插入你自己的错误信息。当你调用 input.setCustomValidity('错误信息') 并传入非空字符串时,该字段会被标记为无效,提交被阻止且提示你写的内容;传入空字符串则清除错误恢复有效。下面演示实时校验用户名长度,并把错误塞进原生校验体系:

<input id="u" required>
<script>
  const u = document.getElementById('u');
  u.addEventListener('input', function(){
    if (u.value.length > 0 && u.value.length < 6) {
      u.setCustomValidity('用户名至少 6 位');
    } else {
      u.setCustomValidity('');
    }
  });
</script>

这种方式的好处是错误提示风格和原生一致,且能和 required、pattern 共用同一套校验时机,不必自己写提交拦截。

原生校验还会给元素加上 :valid 与 :invalid 伪类,你可以据此给错误字段标红边框,无需 JS 也能做视觉提示。但要注意 :invalid 在页面加载时就可能对空字段生效,可用 :user-invalid 或 JS 控制时机,避免用户还没填就被标红造成干扰。

五、只靠前端的局限与正确取舍

必须明确:所有前端校验都能被绕过。用户可禁用 JavaScript、直接改 DOM,或用抓包工具构造请求,原生属性与 JS 校验形同虚设。因此前端的职责是“提升体验和减轻无效请求”,真正的安全边界永远在服务端。实践中建议双层校验:前端用 required、pattern 和 JS 快速反馈,后端对每一次输入再做一次完整校验。很多编程狮读者在面试里被问到这点,记住“前端防君子、后端防小人”即可。选型时,简单必填用原生,复杂或异步用 JS,但无论哪种都不要省略服务端。

对于需要查重用户名这类异步校验,可在 input 事件里用防抖加 fetch 请求,待返回后再用 setCustomValidity 写入结果。注意控制请求频率,避免每次按键都打后端接口;可结合 300 毫秒防抖,既及时又不浪费资源,体验与性能兼顾。

下面给出一个带防抖的实时校验骨架,把频率控制和原生错误提示结合起来,可直接套用到你的项目里:

<input id="user" required>
<script>
  let timer;
  document.getElementById('user').addEventListener('input', function () {
    clearTimeout(timer);
    timer = setTimeout(validate, 300);
  });
  function validate() {
    const el = document.getElementById('user');
    if (el.value && el.value.length < 6) {
      el.setCustomValidity('用户名至少 6 位');
    } else {
      el.setCustomValidity('');
    }
  }
</script>

HTML 表单验证四种方式

实际落地时,先配置好 required、pattern 与 submit 监听这套校验组合,运行表单检查空值与格式是否按预期拦截;如果某次提交失败或提示没出现,对照避坑清单修复监听时机,再在浏览器里验证错误提示是否正确弹出。记住前端校验只是第一道体验防线,真正的合法性仍要由服务端最终裁决。代码按官方文档整理,可直接复用。

总结

HTML 表单验证有四条清晰路径:required 拦空值、pattern 校格式、JS 监听 submit 做复杂判断、setCustomValidity 自定义原生错误。它们由浅入深,能覆盖绝大多数交互场景。但请始终把服务端校验当作最后一道防线,前端只是让体验更顺滑。把常用规则封装成函数或组件,既能复用也能降低遗漏风险,是工程里值得养成的习惯。

把校验逻辑分层后,前端负责即时反馈、后端负责最终裁决,两者职责清晰,协作起来最稳。切忌因为前端写得漂亮就放松后端校验,那是线上数据脏乱最常见的根源之一。

延伸学习

  1. HTML 入门课程 系统学 HTML
  2. HTML5 required 验证笔记 看 required 实战
  3. HTML 教程 复习表单标签

常见问题

Q:前端校验能替代后端校验吗?

A:不能。前端代码运行在用户浏览器里,可以被禁用或篡改,抓包也能直接发非法请求。前端只改善体验,真正的合法性必须由服务端再校验一次,这是安全底线。

Q:pattern 正则不生效可能是什么原因?

A:常见原因是正则写成了带斜杠的完整形式,而 pattern 只接受正则主体;也可能是字段为空时由 required 先拦截,没轮到 pattern。检查书写格式与属性组合即可。

Q:setCustomValidity 填了空字符串还有错?

A:通常是因为没有在合适时机清空,或在事件之外仍保留错误信息。确保校验通过时调用 setCustomValidity('') 解除无效状态。常见疏忽是只在某个分支清空,其他分支忘了,导致字段永久处于无效状态,提交永远被拦截。

0 人点赞