html树形菜单可访问性优化需确保屏幕阅读器准确播报结构且键盘用户能线性可控遍历:role="tree"必须为最外层容器,子列表用role="group",aria-expanded与tabindex须动态同步,方向键逻辑需按展开状态精准判断,aria-level和aria-label必须真实有效。

HTML树形菜单的可访问性优化,核心不是“加几个属性就完事”,而是让屏幕阅读器能准确播报结构、键盘用户能线性可控地遍历所有节点——方向键导航失效,基本等于整个树对键盘用户不可用。
role="tree" 容器必须是直接父级,不能套在 div 里
常见错误是把 <ul role="tree"></ul> 包在一个无语义的 <div> 中,或者用 <code><nav></nav> 套着再加 role。这会破坏 ARIA 树的父子链路,导致屏幕阅读器无法识别层级关系。
-
<ul role="tree"></ul>必须是树结构的最外层容器,且所有role="treeitem"都得是它的直系或嵌套后代 - 子列表必须用
<ul role="group"></ul>(不是<ul></ul>空角色),且只能作为role="treeitem"的直接子元素 - 如果用了
<div role="tree">,必须确保内部结构仍严格匹配 tree 模型,但 ul/li 嵌套仍是兼容性最好、语义最稳的选择 <h3>aria-expanded 和 tabindex="-1" 必须动态同步</h3> <p>只在 HTML 初始写死 <code>aria-expanded="false"不够。JS 展开/折叠时,必须同时操作两个东西:DOM 属性 + 焦点流。否则方向键会跳过隐藏内容,或卡在不可见节点上。- 展开时:设
aria-expanded="true",并把子role="treeitem"的tabindex改为"0"(若之前是-1) - 折叠时:设
aria-expanded="false",并把对应子项tabindex设回"-1",防止键盘焦点落入不可见区域 - 叶子节点不加
aria-expanded;加了反而会让屏幕阅读器误报“可展开”
方向键监听必须区分“移动”和“展开/收起”逻辑
ArrowRight / ArrowLeft 不是简单地“下一个/上一个”,而是有状态判断的:当前节点是否可展开?是否已展开?有没有子项?错一层逻辑,键盘用户就会迷失。
-
ArrowRight:若节点有子项且aria-expanded="false"→ 触发展开;若已展开 → 移入第一个子treeitem -
ArrowLeft:若节点已展开 → 收起;若已收起且非根节点 → 移动到父级treeitem -
ArrowDown/ArrowUp:仅在同级节点间线性移动(跳过tabindex="-1"的隐藏项) - 所有可聚焦节点必须有
tabindex="0",但只允许一个节点处于可 Tab 状态(其余用tabindex="-1"控制)
最容易被忽略的是:aria-level 必须真实反映嵌套深度,不能靠 CSS 缩进“假装”层级;还有,每个
role="treeitem"必须有明确的aria-label或aria-labelledby,否则屏幕阅读器可能只读出“tree item”,不读文字内容。 - 展开时:设











