为什么在通过 JS 向 HTML 文档添加 `svg` 标签时必须使用 `document.createElementNS`?

Why is it necessary to use `document.createElementNS` when adding `svg` tags to an HTML document via JS?

这行不通:

const svg = document.createElement('svg')
svg.setAttribute('height', '100')
svg.setAttribute('width', '100')
document.body.appendChild(svg)

const rect = document.createElement('rect')
rect.setAttribute('height', '100%')
rect.setAttribute('width', '100%')
rect.setAttribute('fill', 'red')
svg.appendChild(rect)

这会起作用:

const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
svg.setAttribute('height', '100')
svg.setAttribute('width', '100')
document.body.appendChild(svg)

const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rect.setAttribute('height', '100%')
rect.setAttribute('width', '100%')
rect.setAttribute('fill', 'red')
svg.appendChild(rect)

显然,每次我通过 JS 创建 svg 标签或其任何后代时,我都需要明确指定命名空间。

Q1:为什么这是必要的?引用 MDN docs on the svg tag:

Note: The xmlns attribute is only required on the outermost svg element of SVG documents. It is unnecessary for inner svg elements or inside HTML documents.

好吧,我在这里将 svg 嵌套在 HTML 中,所以 xmlns 属性应该不是必需的吧?

Q2:这可以避免吗?

每次都手动输入 document.createElementNS('http://www.w3.org/2000/svg', 'rect') 很烦人。

有没有更短的路?

首先,不存在的示例 "work":它按预期工作。很难说是怎么回事,因为没有显示周围的 HTML 文档代码。由于这个问题,我猜解析器(和浏览器)不会将创建的元素解释为 svg 元素。

Q1 - 为什么需要指定命名空间? xmlns 属性为该属性所在的子文档声明一个默认命名空间。每个没有命名空间前缀的元素都属于默认命名空间。如果将 svg 嵌套在 HTML 文档中,我猜默认命名空间将是 HTML 命名空间(任何 HTML 版本都可能),因为在HTML 文档。然后,命名空间感知解析器将读取这些元素,例如 h:rect,其中 h 是 HTML 命名空间的前缀。

Q2 - 可以避免吗?好吧,如果你真的想要,还有其他方法可以解决它,但不避免它要容易得多。换句话说,没有更短的方法可以做到这一点。

我们也来看看你对 svg 的引用 "tag":

Note: The xmlns attribute is only required on the outermost svg element of SVG documents. It is unnecessary for inner svg elements or inside HTML documents.

此说明是关于序列化文档的。它没有说明文档树或它应该如何构建的任何内容。 createElementcreateElementNS 将元素节点添加到文档树中。可以用不同的方式将文档树序列化为 XML 文档,例如使用 xmlns 属性的默认命名空间或使用 xmlns:svg 属性单独声明的每个命名空间等。重要的是您需要 svg 元素的命名空间限定名称。否则它们不会被解释为 svg 元素。如果您在 XML 文档中声明名称空间,XML 解析器将为该元素分配您声明的名称空间。或者反过来,如果您的文档树为该元素定义了命名空间 属性,则文档编写者还将通过使用默认命名空间或命名空间前缀在序列化文档中正确声明命名空间。

如果调用 document.createElement("a") 它应该创建哪个 <a>? HTML 一个还是 SVG 一个?问题就在这里。同上脚本等。在 appendChild 步骤之前,您不会给浏览器任何猜测的方式,到那时就太晚了。

您可以通过 innerHTML 在没有命名空间的情况下执行此操作,因为它需要一个包含所有标记的字符串,因此浏览器可以一步解析和添加,从而推断出正确的命名空间。

或者只创建一个包装函数

function createSVGElement(tag) {
  return document.createElementNS('http://www.w3.org/2000/svg', tag)
}