表单校验

核心不是"标红",而是什么时候报、报给谁看、焦点去哪。 这三件事决定了用户觉得表单"聪明"还是"烦人"。

一、校验时机:别在 oninput 报错

下面两个输入框规则完全一样,只有校验时机不同。都试着打几个字看差别。

反例:oninput 立即报错

最多两位小数

刚敲下 1 就开始红了 —— 但 1 可能是合法的中间状态。 用户打到一半满屏红字,不敢继续打,甚至放弃填表。 这是纯噪音。

正例:blur 才首次校验

最多两位小数,例如 12.34

打字时安安静静。失焦那一刻才校验; 之后你回来修改才会实时提示(因为你已经知道它的规则了)。 这时才叫"帮助"而不是"指责"。

三层时机:onblur 首次校验 → 之后才实时(字段"脏"过)→ onsubmit 全量校验并弹汇总。
🔴 刻意没用 :has():它是 CSS Level 4,某项目 上 WebView 版本不确定, 而且把逻辑硬塞进样式层不可测。控制权交给 JS 加 data-dirty 属性。

第三种状态:异步校验中(field--checking)

正在检查是否可用…

🔴 校验中绝不能把输入框置灰或禁用 —— 用户会以为"不让打字了", 于是停在那里等,而其实他完全可以接着改。 正确做法只有一条:保持可输入,旁边给一个小的 pending 指示。
这个指示器是原地旋转(12px),不是全屏 spinner: 它只说明"这一项在查",不代表"整个页面在忙"。 prefers-reduced-motion 下放慢到 1.2s 但不停(停了会被当成卡死)。

二、完整表单:提交后出错误汇总

什么都不填,直接点提交。焦点会移到汇总上,汇总里每条可点击跳转。

三、错误文案:给建议,不给判断

反例

格式错误

非法输入

值不合法

WCAG 3.3.3 要求给修正建议。这三句用户读完还是不知道该改成什么。

正例

金额最多两位小数,例如 12.34

请填写公司名称,2 到 40 个字

这个日期在未来,请检查

每条都具体、可执行,还带一个正例。

四、无障碍要点

错误汇总:role="alert" + 提交后焦点移到它 —— 否则屏幕阅读器用户不知道页面出现了新东西
汇总里的条目用 <a href="#字段id">, 天然支持键盘和"复制链接",不要用 div onclick
错误文案用 role="alert", 动态出现时会被读出来(aria-describedby 负责聚焦时能听到)
提交成功后焦点必须移走 —— 留在提交按钮上,用户按 Enter 会重复提交