C02 表单校验进阶
摘要:浏览器原生校验能拦住空值与格式,但提示文案不可改、错误分散在字段旁、朗读软件未必播报。要做出可用又可维护的校验,需要四层配合:约束属性负责基础拦截,样式负责视觉反馈,少量脚本负责自定义规则与汇总,服务端负责最终判定。本文给出这套组合的完整代码与三条判定标准。
关键词:约束校验,自定义校验,错误提示,无障碍表单,服务端校验
所属:frontend/html / C-高级 | 上一篇:C01-无障碍与ARIA | 下一篇:C03-资源提示与性能
1 引言
表单校验的失败通常不在「没写校验」,而在分层混乱:把业务规则塞进 pattern,或者只用脚本校验却在脚本失效时全部放行。
本文给出四层校验的分工与一份可运行的实现。范围见表 1-1。
| 项 | 本文覆盖 | 本文不覆盖 |
|---|---|---|
| 约束 | required、type、min/max、minlength、pattern、step | 正则表达式系统讲解 |
| 反馈 | 错误文案位置、aria-invalid、aria-live 汇总 |
设计系统与组件库 |
| 脚本 | novalidate、setCustomValidity、reportValidity |
框架表单状态管理 |
| 服务端 | 校验必要性与错误回传方式 | 接口实现与防重放 |
2 预备知识
| 项 | 要求 | 核对方式 |
|---|---|---|
| 环境 | A02 的本地服务器 | 能访问 localhost |
| 前置 | A07 | 表单控件与 label 关联已掌握 |
| 前置 | C01 | 能使用 aria-live 与名称属性 |
| 文件 | html-practice/validate.html |
编码 UTF-8 |
3 原理与实现
3.1 四层校验的分工
四层校验各自负责不同阶段,越往后越不可省略,如图 3-1 所示。
flowchart TD
A["用户输入"] --> B["约束属性<br/>required / type / pattern"]
B -->|"不通过"| E["浏览器原生提示"]
B -->|"通过"| C["脚本增强<br/>自定义规则与汇总"]
C -->|"不通过"| F["字段描边 + 汇总区文本"]
C -->|"通过"| D["服务端校验"]
D -->|"不通过"| G["回传错误并回填表单"]
D -->|"通过"| H["进入业务流程"]
图 3-1 校验的四层分工
图上可见:前两层都可被绕过,服务端校验是唯一不可跳过的判定点;脚本层的价值在于提示质量,不在于安全。
3.2 脚本增强与可访问提示
<!-- 运行环境:Chrome 130+;body 内内容 -->
<form id="signup" novalidate>
<p>
<label for="email">邮箱</label>
<input
id="email"
name="email"
type="email"
required
autocomplete="email"
aria-describedby="email-error"
inputmode="email"
/>
<span id="email-error" role="alert"></span>
</p>
<p>
<label for="handle">用户名</label>
<input
id="handle"
name="handle"
type="text"
required
minlength="3"
maxlength="16"
pattern="[a-z0-9_-]+"
aria-describedby="handle-hint handle-error"
/>
<span id="handle-hint">3 ~ 16 位小写字母、数字、下划线或连字符。</span>
<span id="handle-error" role="alert"></span>
</p>
<p aria-live="polite" id="summary"></p>
<button type="submit">提交</button>
</form>
<script>
const form = document.getElementById('signup');
const handle = document.getElementById('handle');
const handleError = document.getElementById('handle-error');
const summary = document.getElementById('summary');
const reserved = ['admin', 'root', 'system'];
handle.addEventListener('input', () => {
if (reserved.includes(handle.value)) {
handle.setCustomValidity('该用户名已被保留,请更换。');
handleError.textContent = '该用户名已被保留,请更换。';
handle.setAttribute('aria-invalid', 'true');
} else {
handle.setCustomValidity('');
handleError.textContent = '';
handle.removeAttribute('aria-invalid');
}
});
form.addEventListener('submit', (event) => {
event.preventDefault();
if (!form.checkValidity()) {
const firstInvalid = form.querySelector(':invalid');
summary.textContent = `共 ${form.querySelectorAll(':invalid').length} 项需要修正。`;
firstInvalid.focus();
return;
}
summary.textContent = '校验通过,可以提交。';
});
</script>
约束属性与校验时机见表 3-1。
| 属性 / 事件 | 作用 | 注意点 |
|---|---|---|
required |
必填 | 只拦空值,不拦空格 |
type="email" |
邮箱格式 | 允许 a@b 这类宽格式 |
minlength |
最小长度 | 与 maxlength 成对使用 |
pattern |
正则约束 | 默认整串匹配,不加 ^$ 也可 |
setCustomValidity |
注入自定义错误信息 | 规则解除时必须清空为空串 |
novalidate |
关闭浏览器默认提示,交由脚本处理 | 关闭后仍需保留约束属性 |
checkValidity() |
主动触发校验,返回布尔值 | 不显示浏览器提示 |
reportValidity() |
触发校验并显示浏览器提示 | 用于需要原生样式的场景 |
错误提示的放置方式对比见表 3-2。
| 方式 | 可读性 | 实现成本 | 适用场景 |
|---|---|---|---|
| 浏览器原生气泡 | 一致但不可定制 | 零 | 内部工具、原型 |
| 字段下方内联文本 | 明确,需 aria-describedby |
低 | 大多数表单 |
| 顶部汇总区 | 便于整体判断错误数量 | 中 | 长表单、批量录入 |
| 仅改变边框颜色 | 色觉差异人群不可用 | 低 | 不应单独使用 |
4 验证与讨论
- 留空提交,确认焦点跳到首个错误字段,汇总区显示需要修正的项数。
- 用户名输入
admin,确认出现自定义提示且提交被拦下;改为liming后提示消失。 - 使用朗读软件或无障碍面板,确认错误文本与字段的关联存在。
- 删除脚本(或禁用 JavaScript),提交相同数据,确认约束属性仍然生效。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 焦点定位 | 首个错误字段获得焦点 | 4 节步骤 1 |
| 自定义规则 | admin 被拦下 |
setCustomValidity |
| 错误关联 | 面板显示字段与描述的关联 | aria-describedby |
| 汇总播报 | 播报错误项数 | aria-live="polite" |
| 无脚本可用 | 仍能拦住空值与格式错误 | 约束属性仍在 |
讨论与局限。 setCustomValidity 的错误会一直保留,直到显式清空为空串,忘记清空会导致整个表单无法提交。另 pattern 在不同输入法与移动键盘下行为不完全一致,手机号、证件号这类规则最终仍以服务端为准。
5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 修改后仍提示自定义错误 | 未在规则解除时清空 | 调用 setCustomValidity('') |
| 提交后没有任何提示 | 表单加了 novalidate 但没写脚本 |
补脚本,或去掉 novalidate |
| 错误文本不播报 | 提示容器无 role="alert" 或 aria-live |
补属性 |
| 空格通过必填校验 | required 不校验空白字符 |
提交前 trim() 后再判断 |
| 移动端键盘类型不对 | 未设置 inputmode / type |
邮箱用 type="email",数字用 inputmode="numeric" |
6 结论
| 序号 | 结论 | 依据 |
|---|---|---|
| 1 | 四层校验各负其责,服务端不可省略 | 图 3-1 |
| 2 | 自定义错误必须能被清空 | 表 3-1 |
| 3 | 错误提示需与字段建立描述关联 | 表 3-2 |
| 4 | 禁用脚本后的表现是校验可靠性的下限 | 4 节步骤 4 |
- 上一篇:C01-无障碍与ARIA
- 下一篇:C03-资源提示与性能
- 返回:C00-总结 | 科目 README
参考文献
[1] MDN Web Docs · 客户端表单校验,https://developer.mozilla.org/zh-CN/docs/Learn/Forms/Form_validation(访问于 2026-10-29)
[2] HTML Living Standard · Constraint validation,WHATWG,https://html.spec.whatwg.org/multipage/form-control-infrastructure.html(访问于 2026-10-29)

评论 0
还没有评论,快来抢沙发吧~