aria-expanded是给屏幕阅读器等辅助技术使用的,值为true或false,必须加在触发按钮上而非内容容器,并通过setAttribute动态更新以准确传达展开状态。

aria-expanded 是给谁用的?
它不是装饰,是给屏幕阅读器听的。值为 true 或 false,告诉辅助技术“这个按钮当前控制的内容是展开还是收起”。
常见错误是把它加在内容容器上,比如 <div class="dropdown-menu">,结果屏幕阅读器完全感知不到状态变化。
必须加在触发元素上,典型场景包括:
-
<button>控制折叠面板 - 下拉菜单的 toggle 按钮(如 Bootstrap 的
<button data-bs-toggle="dropdown">) - 移动端 navicon 按钮
如果触发元素没 id,aria-controls 就失效,别漏掉。
怎么更新 aria-expanded 才算有效?
不能只改 CSS 类或 DOM 显示状态就完事。辅助技术不看样式,只认属性值。
必须用 element.setAttribute('aria-expanded', 'true') 或 'false' 显式设置。
以下写法无效或不可靠:
-
element.ariaExpanded = true(非标准属性,IE/旧 Safari 不支持) - 仅切换
hidden属性却不更新aria-expanded - 在 React/Vue 中只更新组件 state,但没同步操作 DOM 属性
尤其注意动画场景:不要在 transition 开始时就设 aria-expanded="true",等 transitionend 或 requestAnimationFrame 确保渲染完成后再更新。
aria-expanded 和 aria-haspopup 要不要一起用?
要,但作用不同,不能互相替代。aria-haspopup 声明“这个元素可能弹出什么”,比如 menu、listbox;它是个静态声明,一般初始化设好就行。aria-expanded 反映“此刻是否已弹出”,是动态状态,必须随交互实时更新。
典型组合写法:<button aria-haspopup="menu" aria-expanded="false">菜单</button><ul id="menu" role="menu" hidden>
两者配合,屏幕阅读器才能准确读出:“菜单按钮,未展开,可打开菜单”。
为什么内容区不能加 aria-hidden?
因为被展开的内容是页面主结构的一部分,不是模态层或临时遮罩。
加 aria-hidden="true" 会让屏幕阅读器彻底跳过它,哪怕它已经 display: block 且可见。
正确做法是:
- 内容区用
role="region"或更具体的role="menu"/role="listbox" - 通过
id与aria-controls关联 - 隐藏时用
hidden或display: none,但确保aria-expanded值同步为false
最常出问题的是 JS 报错中断、CSS 类没清干净、或者状态更新和 DOM 渲染错帧——差一帧,语义就断了。



















