应使用 aria-checked 而非 aria-selected 标识语言切换按钮的当前状态,因语言切换属单选互斥行为,需 role="radio" 语义支持;aria-selected 仅适用于 tab、treeitem 等可多选集合,用于普通按钮违反 WCAG 4.1.2。

怎么用 aria-selected 标识语言切换按钮的当前状态
不能只靠 class 或视觉样式来表达“当前选中”,屏幕阅读器需要明确的语义。直接给语言切换按钮(比如 <button></button>)加 aria-selected="true" 是错的——aria-selected 只适用于 role="tab"、role="treeitem"、role="option" 等有明确“可选集合”语义的元素,普通按钮不支持。
正确做法是:把语言切换控件做成一组 role="radio" 按钮,用 aria-checked 表达当前激活项:
- 每个语言选项用
<input type="radio">(最稳妥)或带role="radio"的<button></button> - 必须共用同一个
name属性(原生 radio)或aria-labelledby+aria-describedby关联组名(自定义 radio) - 当前选中项设置
aria-checked="true",其余为"false";原生<input type="radio">直接用checked属性即可,浏览器自动同步aria-checked
示例(推荐原生方案):
<div role="radiogroup" aria-label="选择界面语言"> <input type="radio" id="lang-zh" name="lang" value="zh" checked> <label for="lang-zh">中文</label> <input type="radio" id="lang-en" name="lang" value="en"> <label for="lang-en">English</label> </div>
为什么不用 aria-selected 而用 aria-checked
aria-selected 描述的是“被选中但不一定激活”的状态,常见于多选表格行、树形节点,用户可同时选中多个;而语言切换是单选互斥行为,属于“当前生效值”,语义上对应 aria-checked(类似单选框、开关)。
立即学习“前端免费学习笔记(深入)”;
把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。
混淆两者的后果:
- 屏幕阅读器可能读作“已选择”,而非“已启用”或“当前为中文”,造成理解偏差
- 某些辅助技术会忽略
aria-selected在非标准角色上的设置,导致状态完全不传达 - WCAG 2.1 要求控件状态必须与角色匹配,否则无法通过
4.1.2 Name, Role, Value条款
用纯 button 实现时怎么避免无障碍失效
如果 UI 强制要求用 <button></button>(比如设计不允许表单提交),必须手动补全语义链:
- 外层容器加
role="radiogroup"和aria-label - 每个按钮加
role="radio"、aria-checked、aria-labelledby(指向组名) - 用 JavaScript 同步更新
aria-checked,并确保键盘操作(Tab/Space)能切换状态 - 禁用原生表单行为:给按钮加
type="button",防止意外提交
关键点:不能只改视觉(比如加 class),必须同步操作 DOM 属性和焦点管理。
容易被忽略的细节:焦点管理与 SSR 渲染
服务端渲染(SSR)时,如果初始 HTML 没正确写出 checked 或 aria-checked="true",首屏对屏幕阅读器就是错的——JS 还没执行,状态已经错了。
- 后端模板或 SSR 框架里,必须根据当前语言上下文,直接输出正确的
checked属性 - 客户端 JS 初始化时,不要覆盖服务端已设好的状态,而是做一致性校验
- 键盘导航时,确保选中项获得焦点(
button.focus()),否则用户按 Tab 进入后不知道当前在哪
语言切换不是视觉组件,是状态控制入口;任何绕过语义、只靠 CSS 或 JS 模拟的做法,都会在语音交互场景下直接失效。


















