aria-controls 必须指向真实存在的唯一 id,不能是 class 或选择器;动态插入菜单需 dom 就绪后设置该属性;多个按钮可共用同一 aria-controls 值,但目标 id 必须唯一且存在;面板销毁时须同步清除 aria-controls;该属性仅声明关联关系,显隐、状态同步和焦点管理需 js 手动实现。

aria-controls 必须指向真实存在的 ID,不能是 class 或选择器
很多人试图用 aria-controls="dropdown-menu" 关联一个 class 为 dropdown-menu 的元素,这是无效的。它只接受 **ID 字符串**,且该 ID 必须全局唯一、已在 DOM 中挂载。哪怕目标元素在视觉上离按钮很远(比如挂在 底部),只要 ID 存在且没被移除,就能关联成功。
动态插入的菜单必须等 DOM 就绪再设 aria-controls
常见错误:JS 创建菜单后立即给按钮加 aria-controls,但此时 document.getElementById("menu-id") 还返回 null。屏幕阅读器会静默忽略这个属性,用户完全感知不到控制关系。
- 正确做法:先
appendChild(menuEl),再triggerBtn.setAttribute("aria-controls", "menu-id") - 更稳妥方案:省略
aria-controls,改用aria-labelledby="trigger-id"在菜单上反向声明归属(尤其适合 Tab 切换类动态加载) - Vue/React 中注意:key 复用可能导致旧 ID 残留,务必在 mount 后重设或清空
aria-controls
多个按钮控制同一面板时,aria-controls 值可以重复
只要它们逻辑上确实控制同一个区域(比如页面顶部和底部各有一个“展开详情”按钮),就允许都写 aria-controls="detail-panel"。但前提是:detail-panel 这个 ID 真实存在,且只被用在那个面板上。
- 禁止多个不同功能的面板共用同一个 ID(如两个下拉菜单都叫
id="menu") - 拼写必须完全一致:短横线
dropdown-menu和下划线dropdown_menu是两个 ID - 面板被销毁时(如路由切换),要同步清除所有关联按钮的
aria-controls属性,否则残留值会让辅助技术解析失败
aria-controls 不等于自动显隐,JS 必须手动同步状态
设置了 aria-controls 后,点击按钮依然不会自动显示/隐藏目标元素。你必须同时操作三件事:
- 切换目标元素的
aria-hidden("true"/"false") - 更新按钮的
aria-expanded(只能是字符串"true"或"false",不能是布尔值或空字符串) - 手动聚焦到菜单第一个可交互子元素(如
menuEl.querySelector('[role="menuitem"]')),否则键盘用户进不去
最常被跳过的其实是焦点管理——aria-controls 本身不接管任何行为,它只是告诉屏幕阅读器“这两个东西有关”,其余全靠你补全。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











