summary 标签本身不支持 role 属性,加了无效且损害可访问性;其原生 role="button" 由浏览器静态绑定,手动覆盖会导致读屏器重复播报、AT 解析冲突及功能异常;应使用 aria-label 或 aria-labelledby 补充语义,而非修改 role。

summary 标签本身不支持 role 属性——加了也无效,还可能破坏可访问性。
为什么给 summary 加 role 是错的
summary 是语义化原生元素,浏览器已为其内置完整角色(role="button")和键盘行为(Space/Enter 触发 toggle)。手动设置 role="button" 或其他值会导致:
- 读屏器重复播报“按钮 按钮”(原生角色 + 手动 role 叠加)
- 部分 AT(如旧版 JAWS)解析冲突,焦点行为异常
- 移除原生语义保障,比如 Safari 中可能丢失箭头图标或 toggle 功能
summary 的 role 由浏览器自动确定,不可覆盖
规范明确要求:summary 必须作为 details 的首个子节点,其语义角色由 UA(用户代理)静态绑定。你不能、也不该通过 JS 动态设置 summary.setAttribute('role', 'tab') 或类似操作——辅助技术只在 DOM 插入时读取一次 role,后续修改完全被忽略。
实测现象:
- Chrome + NVDA:加
role="heading"后仍朗读为“按钮”,不按标题处理 - Firefox + VoiceOver:忽略手动 role,坚持用原生
role="button" - 控制台无报错,但无障碍树中该节点 role 始终显示为 button
真正需要干预的不是 role,而是 aria-label 或 aria-labelledby
当 summary 文本不足以表达上下文(比如仅含图标、或需补充状态),应优先用 ARIA label 类属性,而非改 role:
- 纯图标
summary:加aria-label="展开配置项",比role="button"+aria-label更干净 - 标题含动态状态(如“日志(3 条未读)”):保持原生 role,仅更新文本内容,配合
aria-live="polite"区域更安全 - 避免
aria-labelledby指向外部元素——summary必须自身可聚焦、可朗读,外链 ID 容易导致焦点丢失或朗读中断
记住:summary 的语义契约是固定的;想“增强”它,方向从来不是覆盖 role,而是补全 label、确保文本可读、维持 DOM 位置合法性——这些才是影响真实用户操作的关键点。

















