内联SVG的可访问性必须通过<title>(首个子元素)和<desc>(第二个子元素)实现,二者缺一不可、顺序不可变、不可嵌套;alt属性完全无效。

<title> 必须作为 <svg> 的第一个直接子元素,<desc> 是第二个;缺一不可,顺序错或嵌套在 <g> 里就失效。
为什么 alt 对内联 SVG 完全没用
浏览器不把 <svg> 当作 <img> 处理,所以 alt 属性被直接忽略——哪怕你写了 alt="搜索",读屏器一句都不读。这不是兼容性问题,是规范层面的不支持。
常见错误现象:<svg alt="关闭"><path d="..."></svg> → 读屏器静音,用户完全不知道这是什么。
-
alt只对<img src="x.svg">有效,那是外部 SVG,走图片加载流程 - 内联 SVG 的可访问性入口只有两个:必须存在的
<title>(短描述),和可选的<desc>(补充上下文) -
<title>不等于 HTML 全局title属性——后者在 SVG 里既不触发悬停,也不被读屏器识别
<title> 和 <desc> 必须放在哪、怎么写
它们不是“可以加”,而是“必须作为 <svg> 的前两个子节点”,且不能被任何容器包裹。位置偏移 = 语义丢失。
立即学习“前端免费学习笔记(深入)”;
错误结构:<svg><g><title>删除</title></g><path></svg> → 多数读屏器跳过
正确结构:<svg viewBox="0 0 24 24"><title>删除</title><desc>永久移除该条记录,不可恢复</desc><path d="..."></svg>
-
<title>要简明,信息等价于<img>的alt:比如 “提交表单”,别写 “带箭头的蓝色按钮” -
<desc>补充关键行为或数据含义:“点击后清空所有字段并跳转至首页”,而不是重复<title> - 纯装饰 SVG(如分隔线)仍需空
<title></title>,同时父级加aria-hidden="true"
动态生成 SVG 时最容易漏掉的一步
JS 创建的 <svg> 节点默认没有 <title> 或 <desc>,浏览器不会自动补,也不会继承周围文本。上线前不手动注入,等于彻底放弃可访问性。
示例(DOM 操作):
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');<br>svg.setAttribute('viewBox', '0 0 24 24');<br>const title = document.createElementNS('http://www.w3.org/2000/svg', 'title');<br>title.textContent = '刷新';<br>svg.appendChild(title); // 必须显式 append<br>const desc = document.createElementNS('http://www.w3.org/2000/svg', 'desc');<br>desc.textContent = '重新加载当前数据列表';<br>svg.appendChild(desc); // 顺序不能反<br>svg.appendChild(path); // 图形元素放最后
- D3、React、Vue 等框架渲染 SVG 时,都要确保模板中明确写出
<title>和<desc> - 用
innerHTML插入 SVG 字符串时,字符串里必须已含这两个标签,否则解析后就没了 - 不要依赖 CSS
display: none或visibility: hidden隐藏<title>——它本就不该显示,但必须存在
哪些属性是冗余甚至有害的
role="img"、aria-label、aria-labelledby 这三类属性加在 <svg> 上,不仅没用,还可能干扰读屏逻辑。尤其当 <title> 已存在时,部分读屏器会优先读 aria-label 而跳过真实描述。
- 删掉所有给
<svg>加的role、aria-label、aria-labelledby - 避免把
<svg>包在<div role="img">里——这是双重冗余 - 如果 SVG 本身是按钮(比如有
onclick),直接给<svg>加tabindex="0"和role="button",再配<title>,不要混用aria-label
最常被忽略的其实是 viewBox 合法性:值为 "0 0 0 0" 或负数会导致图形渲染为空白,读屏器虽读出 <title>,用户却看不到对应图标——这是“可访问但不可见”的典型陷阱。



















