浏览 AI使用教程
表单怎样补齐校验、错误、禁用和提交中状态
一个能输入、能提交的表单,还不等于能用。空值、格式错误、接口失败、重复提交和提交中状态,都需要明确反馈和恢复路径。
按实际操作顺序推进
每完成一段就检查当前输出,确认结果符合预期后再继续。
表单最容易漏掉的,往往不是输入框,而是出错以后怎么办
注册表单只有输入框和提交按钮。弱密码提交后页面无反应;重复点击产生两次请求;接口返回邮箱已存在时错误显示在顶部,用户不知道改哪一项。
- 字段与全局错误正确归位
- 重复提交被阻止
- 键盘、焦点和成功跳转已验证
动手前,把材料和不能改的部分定下来
| 需要确认 | 本例内容 |
|---|---|
| 现有材料 | 下方完整HTML仅模拟注册响应,邮箱、密码、协议三字段;无真实接口、邮件或账户创建。不要输入真实密码。 |
| 不能越过的边界 | 客户端提示用于体验,服务端仍执行最终校验;提交中防重复;错误与字段或全局状态正确对应。 |
| 要交付的结果 | 表单状态矩阵与交互验收 |
表单交互状态设计,从“字段与校验”开始做
字段与校验
区分必填、格式、范围、跨字段和服务端规则,错误文案说明怎么修,不只写无效。
触发时机
决定输入中、失焦或提交时校验,避免过早打断,也避免提交后只在页面顶部报错。
提交中与幂等
提交后防重复操作,展示进度;后端使用幂等或唯一约束处理重复请求。
成功与失败
成功说明发生了什么和下一步;失败保留用户输入,区分可重试、需修改和无权限。
键盘与可访问性
标签、描述、错误关联、焦点顺序和按钮状态对键盘与辅助技术可理解。
完整字段关联与异步恢复示例
signup.html
浏览器打开后选择模拟响应
<!doctype html>
<html lang="zh-CN"><meta charset="utf-8"><title>注册状态练习</title>
<form id="signup" novalidate>
<label for="email">邮箱</label><input id="email" type="email" aria-describedby="email-error">
<p id="email-error"></p>
<label for="password">密码(教学值即可)</label><input id="password" type="password" aria-describedby="password-error">
<p id="password-error"></p>
<label><input id="terms" type="checkbox" aria-describedby="terms-error">同意协议</label><p id="terms-error"></p>
<label for="scenario">模拟响应</label><select id="scenario"><option>201</option><option>409</option><option>422</option><option>429</option><option>500</option></select>
<button id="submit">注册</button><p id="message" role="status" aria-live="polite"></p>
</form>
<script>
const el=id=>document.getElementById(id);
let busy=false;
el('signup').addEventListener('submit',async event=>{
event.preventDefault(); if(busy)return;
for(const id of ['email','password','terms']){el(id).removeAttribute('aria-invalid');el(id+'-error').textContent='';}
el('message').textContent='';
const errors=[];
if(!el('email').value || !el('email').validity.valid)errors.push(['email','请输入完整邮箱']);
if(el('password').value.length<8)errors.push(['password','请输入至少8位教学密码']);
if(!el('terms').checked)errors.push(['terms','请确认协议']);
function showError(id,text){el(id).setAttribute('aria-invalid','true');el(id+'-error').textContent=text;}
if(errors.length){for(const [id,text] of errors)showError(id,text);el(errors[0][0]).focus();return;}
busy=true;el('submit').disabled=true;el('submit').textContent='提交中';
try{
await new Promise(resolve=>setTimeout(resolve,800));
const status=Number(el('scenario').value);
if(status===201){el('message').textContent='教学成功:下一步验证邮箱。本例没有真实注册或发邮件。';}
else if(status===409){showError('email','本教学响应表示邮箱已存在,请核对或登录');el('email').focus();}
else if(status===422){showError('password','教学服务端拒绝该密码,请更换');el('password').focus();}
else if(status===429){el('message').textContent='教学限流:稍后重试,不要连续提交';}
else{throw new Error('request-demo-17');}
}catch(error){el('message').textContent='提交失败,输入已保留。教学请求ID:'+error.message;}
finally{busy=false;el('submit').disabled=false;el('submit').textContent='注册';}
});
</script></html>role=status用于结果通知,aria-describedby把说明关联到字段,aria-invalid标错误。只阻止当前页面重复点击;真正接口仍需校验、授权、唯一约束与必要的幂等。
全部用同一注册表单复测
| 操作 | 预期 | 证据 |
|---|---|---|
| 空邮箱提交 | 无模拟请求,焦点在邮箱 | 字段错误与焦点 |
| 合法输入选409 | 邮箱错误,保留其它输入 | aria-invalid及按钮恢复 |
| 选429后再提交 | 全局提示,按钮恢复 | 不变成永久禁用 |
| 选500 | 显示request-demo-17并保留输入 | 异常finally恢复 |
| 选201 | 只显示教学成功,不冒充真实发邮件 | 提示文本 |
完成后的表单状态矩阵与交互验收
预期本地校验失败聚焦首个字段;409关联邮箱,422关联密码,429给全局提示,500保留输入并恢复按钮;201显示验证邮箱下一步。未实际连注册服务,前端防重复不代替服务端幂等。
为什么“错误提示完整,键盘用户仍无法完成”还不能交付
错误提示完整,键盘用户仍无法完成
- 原因
- 焦点、标签与错误关联未实现
- 怎么改
- 使用真实键盘流程和辅助技术检查焦点顺序、aria 描述与提交后定位
