必须用role="img"和aria-labelledby明确图表语义,因canvas/svg默认无图表语义;静态图表适用该组合,复杂SVG可用role="graphics-document";还需提供同步的结构化表格及动态更新ARIA状态。

用 role="img" 和 aria-labelledby 明确图表语义
浏览器和屏幕阅读器不会自动理解 <canvas> 或 <svg> 是图表,必须显式声明其角色。不加 role 的 SVG 元素可能被忽略,或被当作普通容器读出一堆路径坐标,毫无意义。
正确做法是:给图表容器加上 role="img"(适用于静态/语义明确的图表),再通过 aria-labelledby 指向一个可见标题;若需补充细节,用 aria-describedby 指向一段隐藏但可读的描述段落。
-
role="graphics-document"更适合复杂、可缩放、含多层结构的 SVG 图表(如树状图、路线图) - 避免对
<canvas>使用role="img"后只配一个空alt——Canvas 本身不可遍历,必须靠外部文本补全语义 - 标题元素(如
<h2 id="chart-title">)必须真实存在于 DOM 中,不能仅靠 CSS 隐藏又未加sr-only类
为 Canvas 图表提供等效 HTML 表格数据
<canvas> 渲染快、适合动画,但天生不可访问。仅靠 alt 或 ARIA 描述,无法满足需要逐行分析数据的用户(比如使用表格导航模式的屏幕阅读器用户)。
必须提供一份结构化、语义清晰的 <table>,内容与图表完全对应。它不一定要默认显示,但要能被键盘聚焦、被屏幕阅读器识别,并与图表有明确关联。
立即学习“前端免费学习笔记(深入)”;
- 用
aria-owns="data-table-id"将<canvas>与下方<table id="data-table-id">关联 - 表格需包含真实
<thead>/<tbody>,列头用<th scope="col">,行头用<th scope="row"> - 避免用
display: none隐藏表格——改用.sr-only类(position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;)
SVG 图表中启用键盘焦点与方向键导航
SVG 元素默认不可聚焦,即使加了 tabindex="0",也缺乏原生键盘交互逻辑。用户按 Tab 进入图表后,若无法用方向键浏览节点、无法触发 tooltip 或展开子节点,就等于“卡死”在第一个元素上。
关键不是让每个 <circle> 都可 tab 进去,而是设计一组有意义的可聚焦节点(如分组 <g>、数据系列 <path>、图例项),并绑定键盘事件。
- 为关键
<g>添加tabindex="0"和role="group",用aria-label描述其含义(如 “Q3 销售数据组”) - 监听
keydown:右/左箭头在同级节点间切换,上/下箭头用于展开/收起(树状图)或查看相邻数据点(折线图) - 每次焦点移动后,用
aria-live="polite"的区域动态播报当前节点值(例如 “Chrome,市场份额 65.2%”)
动态图表更新时同步刷新 ARIA 状态
图表随用户操作(筛选、时间轴拖动、主题切换)实时重绘时,ARIA 属性和关联文本若未同步更新,会导致屏幕阅读器读出过期信息——比如用户已切到 2024 年数据,却仍听到“2023 年各季度销售额”。
这不是加个 aria-live 就能解决的。需要精确控制什么该读、何时读、读多少。
- 对整个图表容器设置
aria-live="polite"+aria-atomic="false",避免整段描述重复播报 - 数据变更后,仅更新
aria-describedby指向的描述段落内容,并调用element.setAttribute("aria-busy", "true")→ 更新完成 →removeAttribute("aria-busy") - 慎用
aria-live="assertive":它会中断当前播报,仅适用于错误提示或紧急状态变更



















