嵌入方式与CSS尺寸
同一份 SVG 字节,嵌进页面的方式决定它是 文档 还是 图片。交互、无障碍、脚本、能否吃页面 CSS,都在这一层分叉,不在 viewBox 四个数上。
1. 适用 / 不适用
适用: 产品 UI、可点热区、可编辑图形 → inline;图标/插画装饰、用户上传预览 → <img> 或 background;需要 fallback HTML → <object>;要强隔离文档 → <iframe>。
不适用: 不可信内容 inline;需要读屏角色却用 background;需要内部命中却用 <img>。安全边界见 08。
2. 五种嵌入
| 方式 | 脚本 | 指针命中内部节点 | 无障碍 | 继承页面 CSS | 文件内 SMIL |
|---|---|---|---|---|---|
inline <svg> | 会 | 会 | 进当前 DOM 树 | 会 | 会 |
<img src> | 否 | 否(整张图一块) | 靠 alt,无内部树 | 否 | 会 |
<object> | 视同源文档 | 视该文档 | 独立文档 + fallback | 否 | 会 |
<iframe> | 隔离文档 | 隔离 | 隔离 | 否 | 会 |
CSS background-image | 否 | 否 | 无(纯装饰) | 否 | 会 |
「SMIL 会」指 文件自己带的 animate*,不依赖页面样式表。这也是 SMIL 仍有岗位的原因:<img> 里 WAAPI 进不去。
object / iframe 对同源 SVG 几乎是「又开了一份文档」。跨源则按普通嵌入处理,页面 JS 摸不到内部节点。不要靠 contentDocument 当公共 API。
3. CSS 尺寸:盒子不是用户单位
inline 时 <svg> 是普通 CSS 盒。属性 width/height 会变成 intrinsic size,和样式表里的宽高一起打架。响应式优先 只留 viewBox,用 CSS 控盒:
export function fitInlineSvg(
svg: SVGSVGElement,
viewBox: string,
cssWidth: string,
): void {
svg.setAttribute("viewBox", viewBox);
svg.removeAttribute("width");
svg.removeAttribute("height");
svg.style.width = cssWidth;
svg.style.height = "auto";
svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
}
height: auto 能工作,前提是有 viewBox(或显式宽高比)。没有 viewBox 时,引擎把 SVG 当 replaced 元素的默认 300×150 一类盒子,再被 CSS 拉扯——看起来像位图。
<img> / background 的尺寸完全是 图片盒子。内部用户单位只通过 viewBox 映射到这张位图的绘制过程;你无法从页面 CSS 改到某个 circle 的 fill。要主题化图标,inline 或 CSS mask / currentColor 方案,不要指望 img { fill: ... }。
4. 交互与无障碍怎么选
export type EmbedKind = "inline" | "img" | "object" | "iframe" | "background";
export function pickEmbed(input: {
interactive: boolean;
untrusted: boolean;
decorative: boolean;
}): EmbedKind {
if (input.untrusted) return "img";
if (input.decorative && !input.interactive) return "background";
if (input.interactive) return "inline";
return "img";
}
inline 才能在节点上挂 title / role / tabindex,见 03 交互与命中。
可点击热区:命中必须落在内部图形上 → inline。用透明 <img> 叠 DOM 热区是另一条产品线,和 SVG 命中无关。
object 的 fallback 子节点在 SVG 加载失败时出现,适合「矢量优先、位图垫底」。不要把 fallback 当成第二套交互。同源 object 里的脚本会跑;不可信文件不要用 object 冒充「比 inline 安全」——要沙箱用 <img> 或跨源 iframe。
<iframe> 有独立浏览上下文,CSP / 尺寸隔离更完整,代价是通信和焦点。装饰插画不要上 iframe。
5. 失败形态
| 症状 | 原因 |
|---|---|
querySelector("path") 为 null | 嵌在 img / background 里,没有内部 DOM |
| 读屏只听到「图像」 | 可交互图用了 img / background |
页面改 fill 不起作用 | 非 inline,样式进不去 shadow/独立文档 |
| 用户 SVG 弹脚本 | 不可信内容 inline 或同源 object |
| 盒子 300×150 或被拉扁 | 无 viewBox,又靠属性宽高和 CSS 双通道 |
权威资料
核对日期:2026-08-26