foreignObject与HTML嵌入
foreignObject 在 SVG 用户空间里开一个 HTML 视口。适用面很窄:真正需要 HTML 布局、表单控件或系统 IME,并且必须钉在用户坐标上。其余输入框走 Overlay 与 HTML 叠加,不要先上 foreignObject。
1. 适用 / 不适用
适用:
- 流程图节点里的多行编辑,要跟 viewBox 相机一起缩放、一起旋转。
- 必须出现在 SVG 命中树里的
<input>/<select>(极少见;多数 UI 仍该 overlay)。 - 导出「所见即所得」且下游认 HTML 的打印实验(生产 PDF 通常仍应避免)。
不适用:
- 工具栏、模态框、右键菜单——用 HTML overlay,事件所有权更清晰。
- 一句标签、图标文字——用
<text>。 - 整页应用壳。
foreignObject不是「在 SVG 里跑 React」。
系统 IME、候选框、选区、拼写检查:HTML 控件才是真的。SVG <text> 自造 caret 会在 Safari / 移动端失败。
2. 尺寸必须显式
x y width height 是用户单位,不能省略。缺宽高、写 0、或指望 height: auto 撑开,三引擎都会给你空白或裁切。百分比宽高在 SVG 视口上的解析 WebKit 长期不可靠,生产写用户单位数字。
const SVG_NS = "http://www.w3.org/2000/svg";
const HTML_NS = "http://www.w3.org/1999/xhtml";
export function embedHtmlBlock(
parent: SVGElement,
box: { x: number; y: number; width: number; height: number },
html: HTMLElement,
): SVGForeignObjectElement {
const fo = document.createElementNS(SVG_NS, "foreignObject");
fo.setAttribute("x", String(box.x));
fo.setAttribute("y", String(box.y));
fo.setAttribute("width", String(box.width));
fo.setAttribute("height", String(box.height));
html.setAttribute("xmlns", HTML_NS);
fo.append(html);
parent.append(fo);
return fo;
}
HTML 子节点必须是 HTML 命名空间。createElementNS(SVG_NS, "div") 不是 HTMLDivElement,样式和 IME 都不对。动态创建用 document.createElement("div") 或 HTML_NS。从 XML 字符串解析时,子树要带 xmlns="http://www.w3.org/1999/xhtml"。
内部 HTML 的 CSS 像素按该 foreignObject 的用户空间走。相机改 viewBox 时,这块 HTML 会一起缩放——这是你选 foreignObject 而不是 overlay 的理由,也是 IME 候选框容易错位的原因。正在输入时,更稳的是暂时改 overlay(固定 CSS 像素),见 Overlay 章。
3. Safari / WebKit 坑
未实现部分以浏览器为准。WebKit 上长期存在:
- 祖先
transform/ 旋转后裁切、事件偏移。 overflow不为hidden时绘制漏出,为hidden时滚动异常。position: fixed的 HTML 不相对视口,相对这块 foreign 盒子。- 嵌套
<svg>、<iframe>、<video>当子节点,合成和命中不稳定。 requiredFeatures时代的老检测无效;用特性探测:建一个最小 fo,看getBoundingClientRect().width。
生产策略:非 WebKit 可以钉在用户空间的短表单用 fo;Safari 走 overlay + getScreenCTM 对齐。不要赌「以后会修好」把核心编辑放进 fo。
pointer-events 写在 foreignObject 上。内部 HTML 默认抢指针;拖画布经过输入框会丢拖,要在拖拽态把 fo 设成 none,与 Overlay 章同一规则。
4. 安全
foreignObject 能跑任意 HTML,用户 SVG 的 XSS 面包含它。<img src="*.svg"> 不执行;inline 与 foreignObject 会。消毒必须剥 fo 或整段改 overlay 沙箱,见 08 安全与导出。
5. 失败形态
| 症状 | 原因 |
|---|---|
| 一块空白 | 没写 width/height,或为 0 |
| 内部 div 没有 HTML 行为 | 子节点建在 SVG NS |
| Safari 里错位/裁切 | 祖先旋转 + fo;或百分比尺寸 |
| 中文候选框飞出 | 输入框随 viewBox 缩放,IME 按 CSS 像素摆 |
| 用户图标弹了表单钓鱼 | 未消毒的 foreignObject 被 inline |
权威资料
核对日期:2026-08-26