必须同时设置required和aria-required="true",前者触发浏览器校验与提交拦截,后者确保屏幕阅读器明确播报“必填”,缺一则语义链断裂、无障碍支持失效。

aria-required="true" 单独写,读屏听不到“必填”?
不是听不到,是它根本没起作用——aria-required="true 必须和 required 同时出现在同一个 input、textarea 或 select 标签上,否则屏幕阅读器可能忽略、误读,甚至跳过整个字段。
常见错误现象:
- NVDA 或 VoiceOver 朗读“姓名,编辑文本”,但完全不提“必填”
- JAWS + IE 组合下,字段直接被跳过,连名称都不读
- DevTools 里看到
aria-required="true",但检查真实渲染的input节点,required属性根本不存在
原因很简单:aria-required 是辅助技术语义补位,不是校验开关;required 才是浏览器识别“必填”的唯一原生依据。两者缺一,语义链就断了。
为什么必须同时写 required 和 aria-required="true"
分工非常明确:
立即学习“前端免费学习笔记(深入)”;
-
required触发浏览器原生校验:空提交被拦截、弹提示、激活:requiredCSS 伪类、移动端键盘自动切为 email/number 模式 -
aria-required="true"告诉读屏软件:“这个字段用户必须填,别跳过,也别当成可选”——但它对表单提交逻辑完全无感
老版本辅助技术(比如 IE 下的 JAWS)依赖 aria-required 显式声明,而现代浏览器靠 required 就能推导出语义;所以二者共存,才是向下兼容 + 向上稳妥的写法。
反例:<input aria-required="true"> —— 用户点提交,毫无拦截,读屏也可能静默;<input required aria-required="false"> —— 逻辑冲突,部分读屏直接报错或跳过该字段。
React/Vue 等框架里最容易漏掉 aria-required 的地方
自定义组件(比如 <MyInput :required="true" />)往往只把 required prop 透传给外层容器或 wrapper div,真实 input 标签上既没有 required,也没有 aria-required="true"。
实操检查方法:
- 打开 Chrome DevTools → Elements 面板
- 找到最终渲染出来的
<input>元素(不是组件名,不是<div class="input-wrapper">) - 确认该节点同时存在
required属性和aria-required="true"字符串值
动态切换必填状态时更要小心:JS 设置 el.required = true,必须同步执行 el.setAttribute('aria-required', 'true');设为 false 时,不能只删 required,还得显式写 aria-required="false",否则读屏可能仍按旧状态播报。
label 上加星号,读屏能感知吗?
不能。用 label::after { content: " *"; } 生成的星号是伪元素,DOM 里不存在,屏幕阅读器完全读不到。
真正可访问的星号方案只有两种:
- 写成真实文本:
<label>姓名*</label>(最简单,中文习惯后置) - 保留纯文本 label,用
aria-label补充语义:<label aria-label="姓名,必填">姓名</label>
如果必须用 CSS 控制样式(比如高对比度模式下要放大星号),那就得放弃伪元素,改用带 class 的 span:<label>姓名<span class="required-mark" aria-hidden="true">*</span></label>,再配合 aria-label 或 aria-labelledby 确保语义完整。
记住:视觉提示和可访问性提示从来不是一回事,不能靠样式“看起来像必填”就认为完成了标注。



















