跳到主要内容

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