role="tree" 必须与 role="treeitem" 和 role="group" 配合使用,且需正确嵌套、同步 aria-expanded/aria-hidden、手动实现键盘导航并为每个 treeitem 设置连续 aria-level,否则屏幕阅读器无法正确识别树结构。

role="tree" 和 role="treeitem" 必须成对出现
单独写 role="tree" 没有意义,浏览器不会识别为可交互树形结构。必须配合 role="treeitem"、role="group"(用于子节点容器)以及正确的父子嵌套关系,才能被屏幕阅读器正确解析为树。
常见错误是把整个菜单写成一个 div 加 role="tree",里面直接塞一堆 li 或 div —— 这样既不符合 ARIA 规范,键盘导航(如方向键展开/折叠)也完全失效。
-
role="tree"只能用在最外层容器上,且子元素只能是role="treeitem"或role="group" - 每个
role="treeitem"必须有且仅有一个可聚焦的控件(通常是button或带tabindex="0"的元素),用来触发展开/折叠 - 子节点必须包裹在
role="group"里,不能直接挂到treeitem下
aria-expanded 和 aria-hidden 必须动态同步
树节点是否展开,不能只靠 CSS display: none 控制可见性。屏幕阅读器依赖 aria-expanded="true/false" 判断状态,同时对应子 group 的 aria-hidden="true/false" 必须与之严格一致 —— 否则读屏会跳过内容,或误读已隐藏项。
例如:点击展开后,只改了 aria-expanded="true" 却忘了设 aria-hidden="false",NVDA 或 VoiceOver 就会“看不见”子节点。
立即学习“前端免费学习笔记(深入)”;
-
aria-expanded只作用于当前treeitem(表示它自己的子节点是否展开) -
aria-hidden必须加在直接包裹子treeitem的role="group"上 - 初始状态建议显式写
aria-expanded="false"和aria-hidden="true",避免 JS 未加载时逻辑错乱
键盘交互必须手动实现,HTML 不自动支持
写了 ARIA role 不等于获得了方向键导航能力。浏览器不会自动处理 ↑ ↓ ← → 键在树中的焦点移动、展开/折叠逻辑 —— 这些全得自己用 keydown 监听 + 手动管理焦点。
典型缺失点:用户按 → 期望展开当前项,结果没反应;或按 ↓ 跳过整个子树,直接落到下一个同级节点。
- 必须监听
keydown,识别ArrowRight/ArrowLeft控制展开/折叠 -
ArrowDown/ArrowUp需遍历所有treeitem(包括隐藏但未aria-hidden的),按 DOM 顺序移动焦点 - 折叠时,要主动将焦点留在父
treeitem;展开后,焦点应落到第一个子treeitem - 禁用默认滚动行为:
event.preventDefault()很关键,否则按方向键会触发页面滚动
role="tree" 在 Safari + VoiceOver 下特别容易出问题
Safari 对 ARIA tree 的实现比 Chrome/Firefox 更严格。比如:role="treeitem" 缺少可聚焦子元素、group 没包住全部子项、或者 aria-level 层级跳变(如从 level 1 直接到 level 3),都会导致 VoiceOver 完全跳过该节点,甚至整棵树静默。
调试建议:打开 VoiceOver,用 Ctrl+Option+Shift+H 查看当前焦点的 ARIA 层级和属性值,而不是只看 DOM 结构。
- 务必为每个
treeitem添加aria-level,值必须连续递增(父为 1,子为 2,孙为 3) - 避免在
treeitem内部再嵌套role="tree"—— 多层树需用多个独立tree,而非嵌套 - Safari 会忽略没有
aria-label或文本内容的treeitem,哪怕它有button子元素



















