5月27日 15:28

前端面试常问:SVG 怎么做才能让屏幕阅读器也能看懂?

做前端的同学对 SVG 肯定不陌生——图标、图表、动画,哪哪都是它。但面试官一问"SVG 的可访问性怎么做",很多人就卡壳了。这块确实容易被忽略,毕竟视觉上看着没问题就行,谁会去想屏幕阅读器怎么读它?但 WCAG 合规已经在很多地区变成法规要求,不理解这块真说不过去。## 先搞清楚问题在哪SVG 默认对辅助技术不太友好。一个 <svg> 标签丢在页面上,屏幕阅读器可能直接跳过,也可能报一串乱七八糟的路径数据——总之用户体验很糟糕。核心问题就三个:没描述、没角色、没键盘支持。挨个解决就行。## 给 SVG 加上文字描述最基础的做法是在 SVG 内部放 <title><desc> 元素,然后通过 aria-labelledby 关联上去:svg<svg width="200" height="200" role="img" aria-labelledby="chart-title chart-desc"> <title id="chart-title">季度销售柱状图</title> <desc id="chart-desc">显示2024年四个季度的销售数据,Q1为100万,Q2为150万,Q3为120万,Q4为180万</desc> <rect x="20" y="80" width="40" height="100" fill="blue" /> <rect x="80" y="50" width="40" height="130" fill="green" /></svg>````<title>` 写简要名称,`<desc>` 写详细说明。屏幕阅读器会先读标题再读描述,用户就能理解这张图在讲什么。如果 SVG 是通过 `<img>` 引入的,直接写 `alt` 属性就行:`<img src="chart.svg" alt="2024年季度销售柱状图">`。有个常见的坑:有些人会同时写 `aria-labelledby` 和 `aria-label`,觉得双保险。实际上 `aria-label` 优先级更高,会把 `title` 和 `desc` 的内容直接覆盖掉,白写了。二选一就好。## 用 ARIA 角色告诉辅助技术"这是什么东西"SVG 元素本身没有明确的语义角色,需要我们手动指定。常用的就两种场景:**信息性 SVG**(图标、图表、插图)用 `role="img"`:svg **纯装饰性 SVG**(背景花纹、分隔线装饰)用 `role="presentation"` 加 `aria-hidden="true"`:svg装饰性 SVG 千万别加描述,否则屏幕阅读器会读出一堆无意义的内容,反而干扰用户。这个在 WebAIM 的年度调查里是高频错误——很多页面上几十个装饰图标全被读出来,用户听得一头雾水。## 交互式 SVG 必须支持键盘如果 SVG 有点击、拖拽等交互,就必须让键盘用户也能操作。核心就两步:让它可聚焦,让它可触发。**可聚焦**用 `tabindex="0"`:svg **可触发**就是监听 `keydown` 事件,处理 Enter 和空格键:jsconst svg = document.querySelector('svg[role="button"]');svg.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); // 执行交互逻辑 }});焦点样式也别忘了,否则键盘用户根本不知道当前焦点在哪:csssvg[tabindex]:focus { outline: 3px solid #005fcc; outline-offset: 2px;}一个更推荐的做法是直接在 SVG 内部嵌套原生 `<button>` 或 `<a>` 元素,它们自带键盘行为和 ARIA 语义,省去不少额外代码。## 颜色对比度和信息传达WCAG 2.1 要求文本和图形的对比度至少达到 4.5:1(普通文本)或 3:1(大文本和图形元素)。SVG 里的颜色也得遵守这个标准。但比对比度更容易踩坑的是:只用颜色传达信息。比如图表里红绿两色分别代表增长和下降,色盲用户完全分不清。正确做法是加上形状、纹理或文字标签作为第二重区分:css.trend-up { fill: #2e7d32; stroke: #000; stroke-width: 1; stroke-dasharray: none; /* 实线 = 增长 /}.trend-down { fill: #c62828; stroke: #000; stroke-width: 1; stroke-dasharray: 4 2; / 虚线 = 下降 */}## 复杂图表的语义化处理简单图标加个 `aria-label` 就够了,但复杂图表(比如折线图、饼图)光靠一段文字描述很难说清楚。这时候要用分组和角色来构建语义结构:svg 月度销售趋势 折线图显示1月到6月销售持续增长 用 `role="list"` 和 `role="listitem"` 把数据点组织成列表,屏幕阅读器会逐个播报每个数据点的含义,比一段笼统的描述强得多。## 响应式文本也别忽略SVG 里的文字要保证放大后依然可读。用 `viewBox` 配合百分比宽度就行:svg 可读的文本 ```关键是 viewBox 要设,width100%heightauto。这样用户放大页面时文字跟着缩放,不会出现溢出或截断。## 测试才是最终的检验标准代码写得再规范,不上屏幕阅读器跑一遍心里都没底。常见测试组合:- macOS: VoiceOver(按 Cmd+F5 开启)- Windows: NVDA(免费)或 JAWS- 移动端: iOS VoiceOver / Android TalkBack重点关注这几个场景:装饰性 SVG 是否被正确跳过、信息性 SVG 的描述是否准确完整、交互式 SVG 能否用键盘正常操作。自动化工具如 Lighthouse 和 axe 能扫出大部分基础问题,但语义是否准确还得人工验证。面试里被问到 SVG 可访问性,按照"描述 → 角色 → 键盘 → 对比度 → 测试"这个思路答,基本就覆盖了核心考点。实际项目里记得把这些实践落实到组件库和代码规范中,别让可访问性变成上线前才补的债。

标签:SVG