aria-invalid 必须手动设为 "true" 或 "false" 字符串才能被读屏识别,且需配合 aria-describedby 指向可访问的错误提示元素,否则仅设 aria-invalid 无法播报具体错误原因。

aria-invalid 必须手动设为 "true" 才能被读屏识别
浏览器不会自动给输入框加 aria-invalid="true",哪怕你用了 required 或 type="email"。读屏软件(如 NVDA、VoiceOver)只认这个字符串属性,不认 CSS 伪类或自定义 class。常见错误是只改了视觉样式(比如加 class="error"),但 DOM 里根本没写 aria-invalid 属性。
正确做法是用 JavaScript 主动设置:
- 校验失败时:
input.setAttribute('aria-invalid', 'true') - 校验通过时:
input.setAttribute('aria-invalid', 'false')或input.removeAttribute('aria-invalid')(推荐后者) - 值必须是字符串
"true"或"false",不能是布尔值true,也不能是空字符串或数字1
为什么只设 aria-invalid 还不够?必须配 aria-describedby
aria-invalid="true" 只告诉读屏“这个字段错了”,但不会读出错在哪。用户听到“编辑框 无效”就卡住了——得知道原因。这时候 aria-describedby 就是关键桥梁。
它必须指向一个有唯一 id 的错误提示元素,且该元素要可访问:
立即学习“前端免费学习笔记(深入)”;
使用 PptxGenJS 和 Python 解析,将结构化的 HTML/CSS 幻灯片内容转换为原生、可编辑的 PowerPoint(.pptx)演示文稿。
- 错误提示元素(如
<div id="email-error">邮箱格式不正确</div>)不能用display: none或aria-hidden="true"隐藏 - 推荐用视觉隐藏类(如
sr-only),内部含position: absolute; clip: rect(0 0 0 0);等无障碍友好方式 - 最好加上
role="alert"或aria-live="assertive",确保动态插入时能立即播报 - 不要把错误文案塞进
<label>里——aria-describedby不会解析 label 内容
表单提交后 aria-invalid 没反应?检查这几个点
后端返回错误、前端也渲染了红字,但读屏完全静音——大概率是状态没同步到 DOM。
- 是否漏掉了
aria-invalid设置?尤其在异步校验(如用户名唯一性)场景下,容易只更新 UI 文案,忘了改 ARIA 属性 - 是否用了
event.preventDefault()拦截 submit,但没手动触发input.checkValidity()?这会导致:invalid伪类不生效,也常让人误以为 ARIA 同步失败 - 错误提示元素的
id是否和aria-describedby值严格一致?大小写、拼写、前后空格都要对得上 - 是否在用户修改后清除了旧状态?比如没调
removeAttribute('aria-invalid'),导致错误状态滞留
兼容性陷阱:aria-errormessage 不如 aria-describedby 可靠
虽然 aria-errormessage 看起来更“精准”,但它依赖 aria-invalid="true" + 严格匹配 id + 元素未被隐藏,三者缺一不可。而且在 Firefox + NVDA、旧版 JAWS 中常静默。
实际项目中优先用 aria-describedby:
- 所有主流读屏(NVDA、VoiceOver、JAWS)都稳定支持
- 不需要额外判断浏览器组合,代码更简单
- 配合
role="alert"就能保证错误文案被主动、完整播报 - 如果非要上
aria-errormessage,务必同步管理aria-invalid和提示元素的显隐逻辑,别指望它自己“聪明”
真正容易被忽略的不是怎么写,而是每次校验逻辑变更时,是否同步更新了 DOM 属性、提示文案、CSS 类和焦点行为——四者脱节,无障碍就断链了。


















