跳到主要内容

创建元素与命名空间

HTML 文档的默认命名空间是 HTML。SVG 子树必须显式进 http://www.w3.org/2000/svg,否则节点不是 SVGCircleElement,属性不当 presentation attribute,画面空白。

1. 只走 createElementNS

export const SVG_NS = "http://www.w3.org/2000/svg";

export function createSvgElement<K extends keyof SVGElementTagNameMap>(
name: K,
): SVGElementTagNameMap[K] {
return document.createElementNS(SVG_NS, name);
}

document.createElement("circle") 在 HTML 文档里造出的是 HTML unknown 元素,不是 SVG。createElementNS 的第一参数必须是完整 URI,不要写 "svg""SVG"

适用:编辑器、图表运行时、任何动态建树。
不适用:静态内联 SVG 写在 HTML 源码里——解析器会按 SVG 插入模式建树,不必再 createElementNS

2. innerHTML 不是场景图 API

在已经挂上的 SVGElement 上设 innerHTML,HTML 片段解析器 有机会 按上下文造出 SVG 节点。这仍不是生产写入通道:

  • 用户字符串等于 XSS:script、事件属性、foreignObject 里的 HTML,和 inline SVG 同一攻击面。
  • xlink:href、大小写、未闭合标签在 HTML 解析和 XML 解析下结果不同。
  • 你得不到「写入失败」的类型错误,只能看画面缺了一块。

运行时改树:createSvgElement + setAttribute + append。场景对象持有元素引用,见 保留模式与文档树

3. 解析整段 SVG:DOMParser + importNode

export function parseSvgDocument(markup: string): SVGSVGElement {
const doc = new DOMParser().parseFromString(markup, "image/svg+xml");
const err = doc.querySelector("parsererror");
if (err) {
throw new Error(err.textContent ?? "invalid svg xml");
}
const root = doc.documentElement;
if (!(root instanceof SVGSVGElement)) {
throw new Error("root is not <svg>");
}
return document.importNode(root, true);
}

image/svg+xml 走 XML。text/html 会把 SVG 当 HTML 碎片,命名空间和 foreignObject 都不可控。解析结果属于独立文档,必须 importNode 进当前文档再挂载,否则 getCTM() 常为 null

用户上传还要消毒,见 08 安全与导出。解析成功 ≠ 可以 inline。

4. setAttribute vs SVGAnimatedLength.baseVal

presentation attribute 反射成 SVGAnimatedLength 等对象。两条写入通道不要混用:

export function setUserNumber(el: Element, name: string, value: number): void {
el.setAttribute(name, String(value));
}

export function readUserNumber(animated: SVGAnimatedLength): number {
return animated.baseVal.value;
}
通道适用不适用
setAttribute序列化、百分比、"1em"、撤销日志需要立刻拿纯数字且不想触发字符串解析
baseVal.value已经算成用户单位 的数写入 "50%" 这种作者单位;导出时单位信息已丢

baseVal.value 永远是用户单位里的数。你 setAttribute("width", "50%") 之后读到的是相对 viewport 算完的值,写回去会变成绝对数,响应式盒子就锁死。生产:写入保持作者字符串,读取要数字时用 baseVal.valuegetBBox,不要 round-trip 覆盖 attribute。

newValueSpecifiedUnits 能保留单位类型,但撤销/协作仍是字符串更好对账。不要为了「更 DOM」去碰 baseVal 写入。

5. 失败形态

症状原因
新建 circle 不显示,DevTools 里标签像 HTML用了 createElement,没有 NS
el.cxundefined节点不在 SVG 命名空间,没有 SVGAnimatedLength
解析后挂上仍无 CTM忘了 importNode,节点还在 parser 文档里
宽度从 "50%" 变成死像素baseVal.valuesetAttribute 写回去
用户图标弹了脚本innerHTML 灌了未消毒 SVG

权威资料

核对日期:2026-08-26