aria-expanded必须严格同步按钮控制的DOM可见性,且只能加在触发按钮上;需配合aria-controls和role使用,JavaScript更新时须传字符串值,状态变更须与真实渲染一致。

aria-expanded 不是“设一次就完事”的状态标记,它必须和按钮实际控制的 DOM 可见性严格同步,否则屏幕阅读器会播报错误甚至跳过交互。
aria-expanded 必须加在触发按钮上,不能加在菜单容器里
这是最常踩的坑:把 aria-expanded 写在 <ul class="submenu"></ul> 或 <div id="dropdown"> 上,屏幕阅读器根本感知不到状态变化。它只监听触发控件(比如 <code><button></button>)上的值。
- 正确写法:
<button id="nav-toggle" aria-expanded="false" aria-controls="main-menu">菜单</button><ul id="main-menu" role="menu" hidden></ul> - 错误写法:
<button>菜单</button><ul aria-expanded="false"></ul>(属性位置错,AT 无视) - 如果用非语义元素模拟按钮,必须加
role="button"+tabindex="0",但更推荐直接用<button type="button"></button>
JavaScript 更新时必须用字符串值,不能传布尔
setAttribute 的第二个参数必须是字符串 "true" 或 "false",不是 JavaScript 布尔值 true/false。浏览器(尤其是旧 Safari)会把布尔值序列化成无引号的 true,这不符合 ARIA 规范。
- ✅ 正确:
btn.setAttribute('aria-expanded', isOpen ? 'true' : 'false') - ✅ 更简洁(现代浏览器):
btn.toggleAttribute('aria-expanded', isOpen) - ❌ 错误:
btn.setAttribute('aria-expanded', isOpen)(生成aria-expanded="true"无引号,AT 不识别) - ❌ 无效:
btn.ariaExpanded = true(非标准属性,IE/旧 Safari 完全不支持)
状态同步必须对齐真实 DOM 可见性
不能先改 aria-expanded 再改显隐,也不能只改 CSS 类或 display 就不管它。屏幕阅读器只信这个属性值,但它必须反映用户真正能看到什么。
立即学习“前端免费学习笔记(深入)”;
- 判断展开状态应基于可靠依据:用
panel.offsetHeight > 0、!panel.hasAttribute('hidden')或getComputedStyle(panel).display !== 'none',而不是靠 JS 变量缓存 - 隐藏内容时,优先用
hidden属性或aria-hidden="true"+display: none组合;避免仅用visibility: hidden或opacity: 0,它们会让内容仍在可访问树中 - 动画场景下,不要在
transition开始时就设aria-expanded="true";等transitionend或requestAnimationFrame确保渲染完成后再更新 - Safari + VoiceOver 下有时不朗读切换,可在
setAttribute后立刻调用btn.focus()提高播报概率
别忘了配套属性:aria-controls 和 role
aria-expanded 单独存在没有意义。它需要明确告诉辅助技术:“我控制的是哪个区域”,而这个关联靠 aria-controls 和容器的 role 共同完成。
- 触发按钮必须有
id,且aria-controls值要匹配目标容器的id(如aria-controls="submenu-1"→<ul id="submenu-1"></ul>) - 被控容器需设
role="menu"(下拉菜单)、role="region"(折叠面板)或role="tabpanel"(手风琴),不能只靠id存在 - 若按钮文本就是面板标题,建议额外加
aria-labelledby="submenu-1"到容器上,形成双向语义关联 - 对于纯展开/折叠需求,优先用原生
<details><summary></summary></details>—— 它自动处理aria-expanded、键盘操作、焦点流转,无需 JS
复杂点在于:每次点击都得重新校验 DOM 实际状态、更新属性、同步视觉反馈、保障焦点可到达——漏掉任意一环,对屏幕阅读器用户来说,菜单就“哑巴”了。



















