【问题标题】:Why can a DOM element have up to two identical namespace declarations?为什么一个 DOM 元素最多可以有两个相同的命名空间声明?
【发布时间】:2021-09-27 21:42:58
【问题描述】:

TL;DR:为什么我会得到一个序列化的 DOM?

<svg 
  xmlns="http://www.w3.org/2000/svg" 
  xmlns:test="http://example.com/test" 
  xmlns:test="http://example.com/test">
...
</svg>

我需要修改内联到 HTML 页面中的 SVG 图形,这涉及使用命名空间 http://example.com/test 添加元素和属性,命名空间应绑定到前缀 test

原始 SVG 文档可能没有这样的命名空间:

<svg id="img" xmlns="http://www.w3.org/2000/svg">...</svg>

或者它可能已经这样声明了:

<svg id="img" xmlns="http://www.w3.org/2000/svg" xmlns:test="http://example.com/test">...</svg>

所以我想我需要像这样将命名空间添加到 svg 根元素:

svgRoot.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:test", "http://example.com/test");

我很粗心,在每个文档上都执行了这一行,不管命名空间是否已经声明,因为我以为我会用相同的内容覆盖它。但是现在我有很多文档有 0、1 或 2 个相同的命名空间声明。

更令人困惑的是,如果我的源文档有 2 个或更多相同的命名空间声明,然后我添加 1、2 甚至更多,最终相同命名空间声明的总和总是 2,永远不会超过那个。

这种行为在 Firefox 92、Chrome 93 和 Safari 14 中是相同的,因此看起来像是有意的。无论如何,当添加第二个命名空间声明时,Firefox 中的 Web Developer DOM Inspector 莫名其妙地卡住并停止更新 DOM。

这是 2 + 2 = 2 情况的最小示例。请注意,为了简洁起见,我删除了实际使用命名空间绑定的代码:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Namespace Test</title>
</head>
<body>
    This is an SVG graphic:<br />
    <svg id="img" 
        xmlns="http://www.w3.org/2000/svg" 
        xmlns:test="http://example.com/test"
        xmlns:test="http://example.com/test">
        <ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" />
    </svg>
    <script>
        var svgRoot = document.getElementById("img");
        svgRoot.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:test", "http://example.com/test");
        svgRoot.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:test", "http://example.com/test");
        alert("Result: " + svgRoot.outerHTML);
    </script>
</body>
</html>

【问题讨论】:

    标签: javascript html xml svg xml-namespaces


    【解决方案1】:

    这是因为 xmlns:test 在 SVG 命名空间中无效,因此标记中的那个将在 null 命名空间中声明。

    var svgRoot = document.querySelector("svg");
    console.log(svgRoot.attributes[1].namespaceURI);
    This is an SVG graphic:<br />
    <svg
        xmlns="http://www.w3.org/2000/svg" 
        xmlns:test="http://example.com/test">
    </svg>

    那么,JS中的那个就会被正确设置为XMLNS NameSpace:

    var svgRoot = document.querySelector("svg");
    // here the name space is different than in the markup
    svgRoot.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:test", "http://example.com/test");
    console.log(svgRoot.attributes[0].namespaceURI);
    This is an SVG graphic:<br />
    <svg>
    </svg>

    这两个属性都是从不同的 NameSpace 设置的,它们可以共同存在于同一个 DOM 元素上。

    【讨论】:

    • 感谢您的回答!由于代码 sn-ps 我可以看到您是对的,但我仍然不明白为什么,以及在标记中声明命名空间前缀的正确方法是什么。似乎xmlns:xlink="http://www.w3.org/1999/xlink" 有效,xmlns:test="http://example.com/test" 无效。从进一步的实验来看,前缀的选择似乎是有限的(xlink 可以,test 不行),但是 namespaceURI 的选择是任意的。
    • @LenaSchimmel,HTML 语法中的 HTML(5) 仅支持 SVG 和/或 MathML 使用的一组受限制的命名空间声明和属性:html.spec.whatwg.org/#adjust-foreign-attributes。不要期望 HTML(5) 解析器识别任意名称空间声明以及任意名称空间中的元素和属性。
    • 谢谢,@MartinHonnen。现在这对我来说开始有意义了。所以我是否正确地假设存在三种类型的解析器:那些没有任何命名空间支持(pre-XHTML)、那些完全支持命名空间(XHTML)和那些非常有限的命名空间支持(HTML5)?如果我想要适当的命名空间支持,我必须切换到 XHTML?
    • @LenaSchimmel,根据平台和/或编程语言或 API,可能会有打开或关闭命名空间感知的选项(例如,在 Java 世界中,您有 docs.oracle.com/javase/8/docs/api/javax/xml/parsers/…)。在浏览器中,您基本上有一个 HTML 4 或 5 或这些天(简称)用于 MIME 类型 text/html 的 HTML 解析器和一个用于 XML MIME 类型(如 application/xmlapplication/xhtml+xmlimage/svg+xml 和完整的)的 XML 解析器命名空间支持,您需要使用 XML 解析器。
    猜你喜欢
    • 1970-01-01
    • 2015-10-20
    • 2016-01-27
    • 2019-05-12
    • 2012-09-12
    • 2014-08-16
    • 1970-01-01
    • 1970-01-01
    • 2012-11-09
    相关资源
    最近更新 更多