ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Highcharts可访问性实战:从图表选型到多Y轴折线图的无障碍配置

Highcharts可访问性实战:从图表选型到多Y轴折线图的无障碍配置 1. 为什么说选图表库先看可访问性是在给自己避免返工1.1 可访问性服务的不是一小撮人而是一大群真实用户先讲一个我自己踩过的坑。前几年做一个政务数据大屏项目图表、表格、地图全部上齐视觉上很漂亮客户当场验收也很满意。结果上线后没过两周甲方突然转来一封投诉邮件说是残障用户通过市里的无障碍反馈渠道投诉页面里所有图表键盘根本操作不了也无法通过屏幕阅读器读取数据。当时整个研发团队都懵了——验收时压根没人想到这一层。复盘的时候我才认真去查数据才发现可访问性服务的用户群体远比想象中大得多。全球范围内有视力障碍的人口数量相当庞大轻度色觉异常、老花眼、低视力这类用户在普通产品里也占据比例。更别提还有大量习惯用键盘操作页面的效率用户比如键盘快捷键重度使用者以及暂时性手部受伤只能依赖键盘的人。这些人在正常开发流程里往往被默认“忽略不计”但真到合规审查、项目投标、公开上线时一条无障碍不达标就可能成为直接影响项目验收的硬伤。从实际业务看政府网站、银行系统、医疗平台这类 To G / To B 项目无障碍合规经常是明确需求。即便是普通商业项目随着企业社会责任意识增强可访问性改进也越来越多地出现在产品需求池里。我后来每年做技术选型时都会把图表库的可访问性支持情况放到和性能、文档一样重要的位置去评估因为这一项要补往往是最难补的。1.2 等图表画完了再补可访问性成本会高出几个身位很多团队的选型流程是这样的先看哪个库出图效果炫、社区活跃、star 数高然后直接开工。图表组件库一旦铺开全项目里几十张图的配置参数、事件绑定、主题定制全部建立在某个底层库之上这时候再想补可访问性支持就不是改一个属性那么简单了。举一个常见场景假设用的是 Canvas 渲染的图表库所有图像信息都画在一张画布上。对屏幕阅读器来说整张图就是一张无任何语义的图片读屏软件可以朗读的内容只有“图片”两个字数据到底是什么、趋势走向如何、最高值最低值在哪里完全没有办法通过技术手段恢复。要补救的基本方案是在 Canvas 下方隐藏一份 HTML 表格或列表来承载同样数据再通过 ARIA 属性把图像和表格关联。听起来可行但实际做起来要处理 Canvas 内的交互状态翻转、键盘定位高亮位置映射、tooltip 同步显示等问题调试成本非常高。我之前经手过一个系统图表全部跑在 Canvas 上数据还是高频动态刷新的后面为了过无障碍测试前后花了三周人力去补一套旁路描述系统效果还很难说让人满意。所以后来我的选型原则就变成了在项目立项时就去看图表库是否具备原生可访问性能力如果一定要踩坑最好踩在选型阶段而不是踩在上线以后。2. 图表可访问性到底在说什么不只是给读屏软件加一段描述2.1 可访问性在图表场景里分四个层次很多人一提可访问性就想到屏幕阅读器其实图表领域要处理的远不止这一项。我自己在做技术梳理时习惯把图表可访问性拆成四个层次方便项目组按优先级逐项检查。第一层是语义与描述解决“读屏用户能不能知道这张图在讲什么”。包括图表标题、坐标轴说明、数据系列名称、数据值含义以及整张图表的总结性描述第二层是键盘操作性解决“不能使用鼠标的用户能不能完整操作图表”比如通过 Tab 键进入图表区域用方向键逐个读取数据点呼出并查看 tooltip切换图例开关系列显示第三层是视觉可感知性覆盖低视力用户、色觉异常用户图表不能只靠颜色区分关系要支持高对比度主题或者提供图形、文字、纹理等额外编码维度第四层是动态内容通知解决动态刷新场景当图表新增数据或者数据值变化时读屏软件能主动播报更新而不是让用户毫不知情地面对一个静悄悄的新画面。这四个层次各有各的技术方案一个图表库如果能在框架层面直接支持其中大部分项目组就能省下大量原本要“手搓”的工作量。2.2 ARIA 标签与 SVG 语义化是怎么真正起作用的要理解 Highcharts 这类库在可访问性上的优势得先搞清楚浏览器和辅助技术的配合机制。对屏幕阅读器而言普通网页的文字内容可以直接读取但 SVG 图形、Canvas 画布这类元素天然是“语义黑洞”。SVG 里面虽然可以画各种图形、曲线和文字但读屏软件默认并不会把这些矢量路径理解为有意义的数据它只会告诉你这里有一段图形内容具体是什么需要开发者显式地通过 ARIA 属性、role 角色、内部文字节点等方式去补充说明。Highcharts 默认通过 SVG 渲染图表HTML 结构对可访问性的友好程度天生比 Canvas 高。图表内部的标题、副标题、坐标轴标签、系列名称、数据点的文本信息在 SVG 里是真实存在的文本节点理论上读屏软件就有机会感知到。在此基础上Highcharts 的可访问性模块会进一步做语义增强给图表容器增加 role 描述、提供图表总结文本、为每个数据点生成可读的文本描述、把图例和系列数据组织成列表结构。这套机制是库本身设计好的开发者即使不做额外配置默认情况下也能获得比较完整的无障碍语义。这恰恰是很多 Canvas 方案无法低成本实现的。画布渲染库如果想达到同样效果所有语义信息都要另写一套 DOM 结构去模拟等于自己重新实现一部分辅助技术适配层。2.3 色觉异常与高对比度场景里的真实细节图表场景里颜色使用极其频繁线型、柱形、饼图扇区全部用颜色区分。对于红绿色觉异常的群体一张默认红色和绿色系列并列的折线图在屏幕上看起来可能就是两条混沌的线条。可访问性做得好的图表库会提供一整套视觉备用方案。Highcharts 在无障碍模式下支持高对比度模式用户开启后图表系列会改用高对比度颜色方案同时在默认主题里就内置了形状差异化处理比如折线图上给每条线分配不同的 dash 样式。这样一来即使完全分不清颜色的用户也能通过虚线、实线、点线这样的纹理差异来区分不同系列。我后来给一个团队做代码评审时专门强调过图表如果必须用颜色区分信息那就必须附加第二种编码维度这是可访问性视觉层面的底线要求。所以选图表库时除了看是否支持自定义颜色还得看是否内置了这类自适应机制。3. Highcharts 可访问性能力到底强在哪差异是一点一点拉开的3.1 开箱即用的可访问性模块不是说说的了解 Highcharts 的人应该知道从 Highcharts 7.0 开始可访问性作为一个内置模块逐步成为标配。引入方式很简单只需要加载 accessibility 模块然后图表里就能获得包括自动生成图表描述、键盘导航、读屏支持、高对比度模式在内的一系列能力。关键在“不需要额外配置”这一点。我在实际项目里用过不少图表库很多库都能通过插件扩展的方式让你自己写可访问性支持但问题在于“自己写”。团队需要额外掌握 ARIA 规范细节理解 WAI-ARIA Authoring Practices 中关于图表部分的建议还要针对每个图表类型分别定制描述逻辑。有的团队把这些工作放在迭代计划之外一拖再拖最后上线时依然半点无障碍支持都没有。Highcharts 做的事情是把可访问性从“附加项”变成“默认项”。你不需要懂 ARIA 怎么用于图表只要把模块引进来它就会替你把屏幕阅读器需要的内容整理好。模块内部还提供了一些配置项让开发者可以按业务场景自定义描述文本做到既符合规范也不生硬。3.2 主流图表库横向对比差距在真实场景里才看得出来这里拿出市面上几个常见图表库在可访问性维度上做一个比较。比较的维度是我实际测过的不代表官方完整能力清单但对选型有直接参考价值。Highcharts内置可访问性模块覆盖屏幕阅读器支持、键盘导航、高对比度、动态更新通知图表类型覆盖全面配置项丰富适合复杂报表和动态数据场景。EChartsCanvas 渲染为主本身不提供完整的无障碍语义支持核心定位是高性能可视化可访问性需要开发者自行通过额外 DOM 和 ARIA 补充。在社区里能搜到不少“ECharts 如何做无障碍”的帖子但实现方案基本都要自己维护。Chart.js基于 Canvas渲染轻量API 简单可访问性支持较基础官网说明里也承认这是其短板。用于内部工具类场景尚可对外 To C / To G 项目需要仔细评估。D3.js底层图形库提供的是 SVG 操作能力不是完整图表框架。你用 D3 做了一个折线图语义化结构可以做到很好但所有可访问性设计都取决于开发者自己如何构建 DOM、如何加 ARIA。能力强的人可以做得很深入普通团队则很难保证质量。这个对比说明一个问题选型时如果只关注渲染性能和视觉表现很容易忽略掉“默认能力”带来的隐性成本差异。Highcharts 的差异优势本质上不是某个单一功能多强而是它把这套能力集成成了默认体验项目组不需要为了无障碍单独建立额外机制。3.3 动态数据更新时的可访问性处理才是真正拉开差距的地方静态图表的可访问性相对好做无非是把描述文本写好把键盘事件挂上。但真实业务里的图表十有八九是动态的数据轮播、实时刷新、接口拉取后重新绘图这些场景下可访问性面临的挑战要复杂一个量级。屏幕阅读器用户操作网页时依靠的是文档结构的变化通知。动态图表的无障碍体验要做到新数据加载后用户能感知到内容更新更新不能打断用户正在进行的键盘浏览操作tooltip 或信息区域的动态变化不能反复触发无意义的焦点跳转。Highcharts 的动态更新机制和可访问性模块是协同工作的。数据更新后图表会重新生成描述信息并能够通过 live region 区域向读屏软件发出更新通知用户在不操作的情况下也能感知页面变化。这种能力对实时监控大屏、交易行情、设备状态面板这类场景极其关键。我早期做实时数据可视化时曾经用一个 Canvas 库实现实时折线图数据每两秒刷新一次。客户后来反馈读屏用户完全不知道数据在变我们被迫在外面加了一个看不见的 live region 文本标签每次手动插入“数据已更新”的提示。这样能勉强满足读屏通知但和图表数据的关联性很差用户只知道数据变了不知道具体变了什么。换成 Highcharts 这类自带通知机制的方案后更新描述可以直接关联到具体序列和数据指标体验明显不一样。3.4 导出、打印、离线部署这些“角落场景”里也有差距可访问性不只在网页运行时体现。图表经常需要导出成图片、PDF或者嵌入到打印页面、离线部署环境中。很多人没有意识到这些“角落场景”里的可访问性支持差距同样明显。举个例子图表导出成 PDF 后如果图表本身不携带任何文本描述信息导出文档对读屏用户依然不友好。导出成 SVG 时如果 SVG 内部结构杂乱无章、没有分组、没有标题信息后续的加工处理也会很麻烦。Highcharts 的导出功能考虑了这些点导出的 SVG 文件结构本身带有相对清晰的语义层次而很多 Canvas 方案的导出结果是纯图片图片里没有任何可读文本信息后续想补救也没有入口。对于需要定期输出报表的企业项目这一点非常实际。4. 实操给 Highcharts 图表接入完整可访问性支持的步骤记录4.1 确认依赖并引入可访问性模块以我常用的 Highcharts 10 以上版本为例最直接的方式是从 npm 安装然后在入口文件里引入可访问性模块import Highcharts from highcharts; import highchartsAccessibility from highcharts/modules/accessibility; // 注册可访问性模块 highchartsAccessibility(Highcharts);如果你用的是 script 标签引入方式则需要在引入主文件后紧接着引入 accessibility 模块文件script srchttps://code.highcharts.com/highcharts.js/script script srchttps://code.highcharts.com/modules/accessibility.js/script模块加载后Highcharts 默认就会给后续创建的所有图表赋予可访问性支持。这一点我觉得特别值得强调不需要在图表配置里手动加开关只要模块引入了默认就是开启状态。当然你也可以在配置里显式地关闭或调整accessibility: { enabled: true, description: 这张折线图展示了2021至2024年公司各季度的营收变化趋势。 }description 字段是推荐配置的它的作用是为读屏用户提供一段对图表的整体描述。这个描述不是简单重复图表标题而是帮助用户快速理解图表的核心信息。比如“2023年第三季度营收达到峰值随后两个季度持续回落”这种带结论性质的描述对辅助技术用户价值远大于“这是一张折线图”。4.2 多Y轴折线图场景下可访问性配置的细节处理最近“highcharts 多y轴折线图”这个需求的搜索热度很高这也是实际业务里特别常见的场景同一个图表里需要展示两个甚至多个量纲差异巨大的指标比如同时展示销售额万元级和转化率百分比二者数值范围完全不同必须使用多个 Y 轴。先看一个典型的多 Y 轴折线图基础配置Highcharts.chart(container, { title: { text: 销售额与转化率趋势 }, yAxis: [ { title: { text: 销售额万元 }, opposite: false }, { title: { text: 转化率% }, opposite: true } ], series: [ { name: 销售额, type: line, yAxis: 0, data: [1200, 1350, 1480, 1650, 1720, 1900] }, { name: 转化率, type: line, yAxis: 1, data: [3.2, 3.5, 3.1, 3.8, 4.2, 3.9] } ] });这个配置视觉上没有问题但换成可访问性视角问题就来了屏幕阅读器用户在浏览数据点时如果只听到“数据点 3.5”他根本不知道这个值对应的是销售额还是转化率因为两个序列都挂在同一个图表容器下。我的做法是给每条系列单独配置无障碍描述把数据点和对应的量纲关联起来。Highcharts 的 accessibility 模块支持在 series 级别覆盖描述逻辑series: [ { name: 销售额, yAxis: 0, data: [1200, 1350, 1480, 1650, 1720, 1900], accessibility: { description: 销售额序列单位为万元整体呈上升趋势, point: { valueDescriptionFormat: 第{x}季度销售额为{value}万元 } } }, { name: 转化率, yAxis: 1, data: [3.2, 3.5, 3.1, 3.8, 4.2, 3.9], accessibility: { description: 转化率序列单位为百分比季度内波动较小, point: { valueDescriptionFormat: 第{x}季度转化率为{value}% } } } ]这里的 valueDescriptionFormat 是个特别实用的配置它会控制读屏软件逐点朗读时的文本模板。多轴场景下如果没有这种模板配置辅助技术用户很难把数值和指标对应上。单独从官方文档翻到这一项的人不多但在实际项目中价值非常高。另外多轴场景还要注意轴标题的可访问性。Highcharts 默认会把 axis title 作为文本暴露给辅助技术但如果你没有给 yAxis 配置 title读屏软件能获得的信息就会变少所以多轴时一定要把每个轴的 title 写清楚。4.3 键盘导航与图例交互的标准配置方式可访问性模块加载后图表区域会默认支持键盘导航。用户可以通过 Tab 键把焦点移动到图表容器然后使用方向键逐个浏览数据点。键盘导航的默认行为已经不错但在某些交互场景里我建议做一点定制让体验更自然。例如图例默认是响应键盘操作的用户聚焦到图例项后可以按回车切换该序列的显示与隐藏。这个交互对读屏用户了解图表结构特别有用他会知道图表里有两条序列可以独立控制序列的显隐。如果需要自定义键盘导航的提示信息可以通过 accessibility.keyboardNavigation 配置accessibility: { keyboardNavigation: { enabled: true, focusBorder: { enabled: true, style: { borderColor: #0059b3, borderWidth: 2 } } } }focusBorder 这个配置值得注意它能帮键盘用户清晰地看到当前焦点所在的数据点。默认样式有时候不够明显尤其是深色背景的自定义主题下我遇到过焦点框和背景融为一体、用户完全看不到焦点在哪的情况。项目里如果用了自定义主题务必检查这一项。4.4 端到端验证用读屏软件实测一次完整链路配置写得再好最终还是要靠真机验证。我在项目中通常会安排一轮专门的可访问性回归测试测试环境用 Windows 下的 NVDA 和 macOS 下的 VoiceOver浏览器用 Chrome 和 Firefox。测试步骤我一般按这样来第一步用 Tab 键从页面顶部一路往下确认焦点能够进入图表区域。焦点进入后听一下读屏软件是否朗读图表标题和描述信息。第二步在图表区域内使用左右方向键依次浏览数据点。每到一个数据点读屏应该读出类似“第2季度销售额为1350万元”这样的完整信息而不是只读出一个孤零零的数字。第三步导航到图例项使用回车键切换序列显示状态。切换后焦点不应当丢失读屏应该给出序列状态变化的提示。第四步如果项目支持动态数据刷新刷新后确认读屏有播报且焦点位置没有被强制跳走。实际做下来这一步能发现很多配置层面的小问题。我印象最深的一次是自定义了 tooltip 的 formatter结果在高版本里序列的数据点描述被覆盖了读屏用户按方向键听到的全是 undefined。这种问题只在真机测试时才会暴露纯靠代码审查很难发现。5. 常见问题与排查技巧实录5.1 模块引入了但读屏软件完全读不出图表内容这个问题很常见大部分情况不是配置错误而是引入顺序问题。Highcharts 的可访问性模块必须在主库之后加载如果顺序反了模块可能静默失败代码不报错但功能没有生效。还有一类情况你在初始化图表后又通过 chart.destroy() 销毁并在同一个容器上重新创建了图表实例。Highcharts 的可访问性模块依赖图表创建时注册的一系列内部事件销毁重建时容器内的一些描述节点可能没有清理干净导致新的描述文本不输出。遇到这种场景建议在销毁后主动清空容器的 innerHTML 再重新初始化。5.2 数据动态更新后读屏软件没有播报变化Highcharts 组件默认对动态数据更新的播报有内部机制但如果你在代码里通过 chart.series[0].setData(newData, true) 这类方式更新数据而没有保留 accessibility 模块对更新通知的劫持就可能出现更新后不播报的情况。排查时先确认 accessibility 模块是否在 Chart 实例创建前完成注册。其次检查是否手动覆盖了 chart 容器的 aria-live 相关属性如果容器上被覆盖了 aria-hiddentrue那么无论模块内部怎么发通知读屏软件都不会读取。我遇到过一种特殊情况页面里有多个动态图表其中一张图更新正常另一张图不播报。后来发现是第二张图的容器被某个第三方组件设置了 aria-hiddentrue这个属性连同图表描述节点一起屏蔽掉了去掉后恢复正常。5.3 自定义 tooltip 后数据点描述信息丢失给图表写了自定义 tooltip formatter 后数据点朗读信息变成“未命名点”或者直接不朗读这种情况在社区里被问过很多次。reason 是可访问性模块依赖内部维护的一套点描述逻辑而自定义 tooltip 会改变点的格式化路径。如果你对 tooltip 的展示样式有强需求建议在配置 accessiblePoint 相关属性时也同步补充点的描述信息或者复用 valueDescriptionFormat 模板来生成 tooltip 内容这样能确保视觉展示和读屏描述走同一套数据模板两个渠道的信息保持一致。5.4 浏览器与读屏组合差异导致的效果不一致读屏软件的兼容性不像浏览器标准那么整齐。同样一段带 ARIA 的图表结构在 NVDA Chrome 下正常朗读换到 VoiceOver Safari 可能就只读标题不读数据。这里没有一劳永逸的配置能解决所有组合的问题我的经验是主测组合定在 NVDA Chrome这是 Win 用户最主流的搭配然后补测 VoiceOver Safari 覆盖 macOS 用户。两个组合都通过的情况下基本能满足绝大多数辅助技术用户的真实使用场景。如果项目有特殊合规要求再根据目标用户群体增加对应读屏的专项测试。5.5 关于自定义主题和品牌色的额外提醒很多项目会深度定制图表主题把主色调改成品牌色。但自定义主题时如果完全偏离 Highcharts 默认调色板很容易出现可访问性退化相邻序列颜色对比度过低或者序列与背景的对比度不满足 WCAG 标准。排查这一问题时可以用浏览器开发者工具里的颜色对比度检查或者用 Highcharts 主题配置里自带的 contrastColor 属性来保证文字与背景的对比度。举个例子如果柱形图背景是深蓝色柱子上的数据标签文字是深灰色几乎看不清这时候配置 dataLabels 的 style.color 为对比度更高的浅色即可。这些细节虽然是视觉层面的但直接影响低视力用户的使用体验也会被无障碍检测工具抓出来。6. 这块能力后续还能怎么持续发挥作用可访问性这个能力一旦建好受益的不只是障碍用户群体。我在一个大型后台项目里把 Highcharts 的可访问性模块配置沉淀成了一份团队内部的图表组件规范后键盘导航、读屏描述、高对比度支持成了所有新增图表的默认配置后面接手的同事不需要重新研究这些概念直接按规范做就行。后续如果团队想把这块继续做深可以沿着三个方向扩展第一把图表描述文本的生成逻辑接入业务数据字典让描述信息自动带上业务术语而不是干巴巴的指标名第二结合自动化测试工具把可访问性检查写进 CI 流程每次构建后自动跑一轮 DOM 结构校验第三主动收集真实辅助技术用户的反馈不同人群的操作习惯差异很大靠开发团队自己模拟场景始终会有盲区。我个人在实际项目里的体会是可访问性做得好不好本质上是检验一个团队有没有真正把用户当完整的人来对待。图表库选型时多花半天时间对比一下可访问性能力大概率能在项目后期省下按周计算的返工时间这笔账怎么算都不亏。
返回列表