单选框可访问性差主因是未用fieldset+legend包裹或label未正确关联;fieldset明确逻辑分组,legend提供可访问标题,label须显式关联或嵌套,name值须逐字符相同。

单选框可访问性差,八成是因为没用 fieldset 和 legend,或者 label 没和 input 正确关联。光靠 CSS 排版或视觉分组,对屏幕阅读器完全无效。
为什么必须用 fieldset + legend 包裹单选组
屏幕阅读器不会把一堆孤立的 input[type="radio"] 当作一个逻辑单元。只有 fieldset 才能明确告诉辅助技术:“这是一组互斥选项”,而 legend 是这组的可访问标题——它会被读作“性别:单选按钮,共2项”,而不是“男、女”两个零散词。
-
legend必须是fieldset的第一个子元素,不能用div或h3替代 - 如果视觉上不想显示
legend文字,用position: absolute; clip: rect(1px, 1px, 1px, 1px);隐藏,绝不能用display: none或aria-hidden="true" - 漏掉
legend或留空,Lighthouse 会直接报错“fieldsetmissing legend”
label 怎么写才真正可点击、可读取
只写 <input type="radio" name="theme"> 暗色 是无效的。屏幕阅读器不知道“暗色”对应哪个控件,键盘用户也无法用 Tab 键聚焦后按空格选中。
- 推荐用显式关联:
<input id="theme-dark" type="radio" name="theme" value="dark"><label for="theme-dark">暗色</label>,id和for值必须完全一致(包括大小写) - 嵌套方式也行:
<label><input type="radio" name="theme" value="light"> 亮色</label>,但注意不要在中间插入换行或空格,否则部分旧浏览器可能断开关联 - 避免给
label加tabindex="0"—— 它本就可聚焦,加了反而破坏原生行为
name 属性写错会导致分组失效
看似只是拼写问题,实际会让整组单选变成多个独立控件。比如 name="payment-method" 和 name="payment_method" 在浏览器眼里是两组,用户可以同时选中“支付宝”和“微信”——后端收到两个值,逻辑就崩了。
立即学习“前端免费学习笔记(深入)”;
- 同一组所有
input[type="radio"]的name值必须**逐字符相同**,包括连字符、下划线、大小写 - 别依赖 JS 动态拼接
name,容易因空格或编码差异出错;服务端校验时也要严格比对原始字符串 - 如果一组里只有一个选项(极少见),仍要保留
fieldset+legend,否则语义断裂;但可不设checked,避免强制用户选择
JavaScript 动态操作时容易忽略的可访问性点
用 JS 切换默认选中、禁用某选项、或根据条件显示/隐藏单选组,很容易破坏焦点流和状态同步。
- 设置选中状态别只改
checked属性,还要确保该元素已渲染且未被disabled;否则屏幕阅读器可能读不到变化 - 禁用某个选项时,用
input.disabled = true,别只加opacity: 0.5或pointer-events: none—— 键盘用户仍能 Tab 进去,但无法操作 - 如果整组被 JS 隐藏(如条件单选),记得同步更新
aria-hidden="true"并移除tabindex,否则焦点会卡在不可见元素上
最常被跳过的其实是 legend 的语义重量:它不只是个标题,而是整组控件的“可访问名称”。哪怕视觉设计要求极简,也不能省略或弱化它——因为对某些用户来说,那是唯一能理解这组选项在干什么的文字。



















