最可靠的方式是给<input>添加aria-describedby并指向错误提示元素的唯一id,且该元素需设role="alert"或aria-live="assertive",同时确保其在DOM中存在、未被CSS隐藏(须用视觉隐藏类),并与aria-invalid="true"成对使用,校验通过时同步移除二者。

用 aria-describedby 关联输入框和错误提示最可靠
直接给 <input> 加 aria-describedby 属性,值设为错误提示元素的 id,是目前所有主流屏幕阅读器(NVDA、VoiceOver、JAWS)都稳定支持的方式。比 aria-errormessage 兼容性好得多,后者在旧版 JAWS 和部分移动 VoiceOver 中可能被忽略。
常见错误是把提示文字写死在 title 或 placeholder 里——这些不会被读屏软件当作错误信息播报,也不支持动态更新。
<input id="email" aria-describedby="email-error"><div id="email-error" class="error-message" role="alert">请输入有效的邮箱地址</div>- 错误提示元素必须有
role="alert"或aria-live="assertive",否则变化时可能不主动播报
错误提示元素不能 display: none 或 visibility: hidden
即使用了 aria-describedby,如果错误提示元素被 CSS 隐藏(比如 display: none),屏幕阅读器会直接跳过它,关联失效。这是实际项目中最常踩的坑。
正确做法是用视觉隐藏但保留可访问性的 CSS:
立即学习“前端免费学习笔记(深入)”;
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
然后通过切换类名控制显隐:class="error-message visually-hidden" → 移除 visually-hidden 即可让提示“出现”且被读屏识别。
动态显示错误时要确保 DOM 顺序和状态同步
如果错误提示是 JS 动态插入的,必须保证:① 插入后立即存在 DOM 中;② aria-describedby 的值与当前存在的 id 严格一致;③ 不要复用同一个 id 给多个提示元素(ID 必须唯一)。
典型问题场景:表单校验失败后,JS 创建新 <div id="email-error"> 并插入,但旧的同名元素没删干净,导致 ID 重复,读屏可能随机选一个播报或完全静默。
- 每次显示新错误前,先
document.getElementById('email-error')?.remove() - 插入后手动触发一次
input.focus(),能强制多数读屏重读描述内容 - 避免用
innerHTML = ''清空整个容器再重写——容易漏掉已挂载的id元素
不要依赖 aria-invalid="true" 单独工作
aria-invalid="true" 只告诉读屏“这个字段无效”,但不说明为什么无效、该怎么改。它必须和 aria-describedby 配合使用,否则等于只报错不给提示。
有些开发者以为加了 aria-invalid 就够了,结果测试时发现 VoiceOver 只说“无效”,然后沉默——因为没关联具体错误文本。
- 始终成对使用:
aria-invalid="true"+aria-describedby="xxx" - 校验通过时,记得同时移除
aria-invalid和aria-describedby,否则读屏仍会播报旧错误 - 不要用
aria-invalid="grammar"这类值——只有true/false是广泛支持的
id 清理或一次 visually-hidden 切换,无障碍提示就会断链。



















