首页 > 解决方案 > 为什么一个 DOM 元素最多可以有两个相同的命名空间声明?

问题描述

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>

标签: javascripthtmlxmlsvgxml-namespaces

解决方案


这是因为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 元素上。


推荐阅读