表单错误、状态反馈与动态内容
表单错误必须同时服务于视觉、焦点和读屏用户。输入框用 aria-invalid="true" 表示当前错误,用 aria-describedby 关联具体错误文本;提交失败后把焦点移到摘要或第一个错误字段,并保留修正路径。
html
<input id="name" aria-invalid="true" aria-describedby="name-error" />
<p id="name-error" role="alert">请输入姓名</p>role="alert" 适合短暂、重要的反馈;列表刷新、后台进度等不应每次都抢焦点,可以用 aria-live="polite" 并控制更新频率。
练习
设计一个异步保存按钮:提交中禁用重复提交,成功和失败分别给出可读反馈,失败时焦点不应跳到页面顶部。
核心机制与实现
无障碍实现是语义、键盘、焦点、名称、状态和动态反馈的组合;ARIA 只能补足缺失语义,不能替代原生元素。
围绕“表单错误、状态反馈与动态内容”先写出输入 → 中间状态 → 可观察结果,再实现最小闭环。不要把事件监听、异步请求、DOM 读取或 store 写入混在一个函数里;每个副作用都要有创建、完成、取消和清理路径。
html
<button aria-expanded="false" aria-controls="menu">菜单</button>
<div id="menu" hidden>...</div>
<script>
button.addEventListener('click', () => {
const open = button.getAttribute('aria-expanded') === 'true'
button.setAttribute('aria-expanded', String(!open))
menu.hidden = open
})
</script>逐步运行:先只验证成功路径,再改变一个输入;记录时间顺序、对象身份、错误类型和最终可见状态。若环境不支持某个浏览器 API,要把静态协议验证和真实浏览器验证分开记录。
失败边界与变体
- 输入为空、重复、过期或非法时,不应静默覆盖有效状态。
- 组件卸载、路由离开、标签页关闭或请求取消时,资源必须停止写入。
- 跨文档、Shadow DOM、Worker、SSR 或多标签页会改变可见边界,不能沿用单页面直觉。
- 需要持久化时记录 schema 版本和迁移失败策略;需要全局状态时先证明局部状态不足。
可运行实验与练习
使用键盘完成打开、移动、关闭和返回焦点;用辅助技术或浏览器无障碍树核对名称、角色、状态、错误和动态更新。
练习要求:实现一个最小版本,补一个失败测试,再给出一个替代方案及其适用条件。答案应包括实际输入序列、观察结果、修复原因和未覆盖的浏览器差异。