aria-expanded 是 aria 布尔属性,值必须为字符串 "true" 或 "false",用于向屏幕阅读器准确传达折叠控件(如手风琴、下拉菜单)当前展开状态;必须加在触发按钮上,通过 setattribute 动态同步 dom 显隐状态,不可依赖默认值或非标准赋值方式。

aria-expanded 是什么,什么时候必须手动设
aria-expanded 是一个 ARIA 属性,用于告诉辅助技术(比如屏幕阅读器)某个可展开/折叠的控件当前是否处于“展开”状态。它本身不控制 DOM 行为,只传递语义——也就是说,aria-expanded="true" 不会让元素自动显示,aria-expanded="false" 也不会让它隐藏。你得自己用 JS 控制显隐,再同步更新这个属性。
常见使用场景:手风琴菜单、下拉导航、折叠面板(details 除外,它原生支持,无需手动设 aria-expanded)。
怎么用 JS 正确设置 aria-expanded
关键点是「状态同步」:DOM 可见性变化后,必须立刻更新 aria-expanded 值,否则屏幕阅读器会读错。
- 初始状态要明确设——不要依赖默认值,显式写
aria-expanded="false"或aria-expanded="true" - 切换时用
element.setAttribute('aria-expanded', 'true')或element.setAttribute('aria-expanded', 'false'),避免用element.ariaExpanded = 'true'(部分浏览器不兼容) - 如果用 class 控制显隐(如
.is-open),记得在 class 切换后立即更新aria-expanded,别写在动画完成回调里(辅助技术不等动画)
示例:
const toggleBtn = document.querySelector('[aria-controls="panel-1"]');
const panel = document.getElementById('panel-1');
toggleBtn.addEventListener('click', () => {
const isExpanded = panel.getAttribute('aria-expanded') === 'true';
panel.setAttribute('aria-expanded', String(!isExpanded));
panel.hidden = isExpanded;
});
容易踩的坑:false / "false" / null 的区别
aria-expanded 是布尔属性,但它的合法值只有两个字符串:"true" 和 "false"。其他任何值(包括空字符串、null、undefined、数字 0 或 1)都会被辅助技术忽略或误读。
- ❌ 错误:
el.setAttribute('aria-expanded', false)→ 实际设的是字符串"false",看似对,但类型易混淆;更糟的是el.removeAttribute('aria-expanded'),这会让属性消失,等价于未定义,不是false - ✅ 正确:始终用字符串
"true"或"false" - ⚠️ 注意:用
el.hasAttribute('aria-expanded')检查存在性没问题,但判断状态一定要用el.getAttribute('aria-expanded') === 'true',不能用!!el.getAttribute('aria-expanded')
和 role="button" 配合使用的必要性
如果一个元素靠 JS 实现展开/折叠,但它本身不是原生按钮(比如用了 <div> 或 <code><span></span>),就必须加 role="button",否则屏幕阅读器不会把它识别为可交互控件,即使有 aria-expanded 也无效。
- 同时加
tabindex="0",保证键盘可聚焦 - 监听
Enter和Space键触发,符合无障碍规范 - 不要只依赖
onclick—— 键盘用户需要onkeydown处理
正确结构示例:
<span role="button" tabindex="0" aria-expanded="false" aria-controls="panel-2">更多信息</span> <div id="panel-2" hidden>内容…</div>实际项目里最常漏掉的不是设属性,而是没同步更新、没配
role、或者用错了真假值类型。这三个点卡住,整个组件对屏幕阅读器就等于不存在。











