
当无法使用原生 / 时,可通过 role="list" 和 role="listitem" 构建语义化嵌套列表;关键在于严格遵循 ARIA 列表嵌套规范:子列表必须直接包裹在 listitem 内,且所有直接子元素都必须有明确角色。
当无法使用原生 `
- `/`
- ` 时,可通过 `role="list"` 和 `role="listitem"` 构建语义化嵌套列表;关键在于严格遵循 ARIA 列表嵌套规范:子列表必须直接包裹在 `listitem` 内,且所有直接子元素都必须有明确角色。
在无障碍(Accessibility)实践中,屏幕阅读器(如 NVDA、JAWS、VoiceOver)依赖正确的 ARIA 角色嵌套关系来识别列表层级与项目数量。你提供的原始代码失败的根本原因在于违反了 ARIA 列表的结构约束:
<!-- ❌ 错误示例:role="list" 的直接子元素缺少 role="listitem" --> <div role="list"> <div> <!-- ❌ 没有 role,不是有效 listitem --> <div role="listitem"></div> <div role="listitem"></div> </div> <div> <!-- ❌ 同样缺少 role --> <div role="listitem"></div> <div role="listitem"></div> </div> </div>此处
<div role="list"> 有两个直接子 <code><div>,但它们未声明 <code>role="listitem",因此屏幕阅读器仅将这两个容器识别为“2 个元素”,而非“4 个列表项”。role="listitem"不是可选装饰——它是role="list"的必需子角色,且必须是其直接子元素。✅ 正确做法是:
- 所有
role="list"的直接子节点必须是role="listitem"; - 嵌套子列表(
role="list")必须置于某个role="listitem"的内部内容中(模拟<li><ul>...</ul></li>的 DOM 关系); - 禁止出现无角色的中间容器(如包裹多个
listitem的匿名<div>); <li>文本内容应直接置于 <code>listitem内,或通过辅助标签(如<span></span>)包裹,避免语义丢失。
以下是符合规范的嵌套结构示例:
<div role="list"> <div role="listitem">Vegetables <div role="list"> <div role="listitem">Tomatoes</div> <div role="listitem">Lettuce</div> <div role="listitem">Broccoli</div> </div> </div> <div role="listitem">Fruit</div> <div role="listitem">Meat</div> <div role="listitem">Bread</div> </div>? 屏幕阅读器行为说明(以 NVDA 为例):
- 读到外层
list时,会宣布:“list, 4 items”; - 进入第一个
listitem(“Vegetables”)后,紧接着检测到其内部的role="list",会自然过渡并宣布:“list, 3 items”——不触发“out of list”提示,从而明确传达“这是子列表”而非独立列表; - 若错误地将子
list放在list的同级位置(如原始代码),则子list会被忽略或降级为普通容器,导致计数错误与层级混淆。
⚠️ 重要注意事项:
- 不要滥用
role="group"替代listitem:group无列表语义,不会被计入list项数; - 避免在
listitem中嵌套非语义容器(如无role的<div> 包裹文本),可能截断可访问性树; <li>如需视觉缩进,应通过 CSS(如 <code>margin-left或padding-inline-start)实现,而非依赖 DOM 层级; - 始终优先选用原生
<ul></ul>/<ol></ol>/<li>—— 它们自带完整语义、键盘导航(如上下键遍历)和跨浏览器一致性;仅在样式或框架限制等强约束下才退而使用 ARIA 列表。
总结:ARIA 列表不是“用
div+role模拟标签”,而是严格复现原生列表的语义契约。只要确保list → listitem → (optional: list → listitem)的线性嵌套链完整、无跳级、无匿名父容器,屏幕阅读器即可准确播报层级与数量。 - 所有










