aria-controls 仅建立可选语义关联,非强制绑定;动态加载 tab 内容时,应仅对激活 tab 设置该属性,避免指向不存在的 dom 元素,推荐用 aria-labelledby 反向关联或二者择一使用。

aria-controls 的作用不是强制绑定,而是建立可选的语义关联——它告诉屏幕阅读器“这个 tab 点击后会控制哪个内容区域”,但不负责 DOM 存在性、加载时机或状态同步。
什么时候必须用 aria-controls?
其实没有“必须”的场景。ARIA 规范明确写的是 SHOULD,不是 MUST。尤其在动态加载 Tab 内容时,如果所有 tab 元素都硬编码 aria-controls="panel-2",而 panel-2 当前根本不在 DOM 中(比如内容还没加载),屏幕阅读器可能报错、跳转失败,甚至卡住。
此时更稳妥的做法是:
- 只给当前激活的 tab 设置
aria-controls,其他未激活 tab 不设该属性 - 或完全不用
aria-controls,改用tabpanel上的aria-labelledby反向关联 - 确保每次切换 tab 时,对应
tabpanel已插入 DOM,且 ID 真实存在
aria-controls 和 aria-labelledby 能不能同时用?
可以,但没必要。两者是互为替代的关联方式:
-
tab上设aria-controls="panel-id"→ 正向:导航项“控制”内容区 -
tabpanel上设aria-labelledby="tab-id"→ 反向:内容区“被哪个 tab 标题描述”
选一种即可。若内容区标题文本和 tab 文本一致(如都叫“报告查询”),aria-labelledby 更自然;若 tab 是图标按钮、无可见文本,则必须用 aria-label + aria-controls 组合。
为什么 JavaScript 更新 aria-controls 后屏幕阅读器没反应?
因为 aria-controls 本身不触发通知。它只是静态声明关系,不带“状态变化”语义。真正需要被朗读或感知的,是 aria-selected、aria-expanded 或 aria-live 区域的变化。
所以关键不是改 aria-controls,而是:
- 切换 tab 后,立刻设置
tab的aria-selected="true" - 同时把焦点移到对应的
tabpanel(用focus()) - 如果内容是异步加载的,在 DOM 插入完成后,再触发一次
aria-live="polite"提示(例如在tabpanel内加一个隐藏的status区)
最易被忽略的一点:很多人以为写了 aria-controls 就等于完成了无障碍支持,其实它只是“关系声明”,真正的可访问体验取决于焦点管理、状态同步和 DOM 实时性——这三个环节漏掉任何一个,aria-controls 就只是个摆设。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











