Why can a DOM element have up to two identical namespace declarations?

Viewed 76

TL;DR: Why can I end up with a DOM that serializes to this?

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

I need to modify an SVG graphic that is inlined into a HTML page, which involves adding elements and attributes using a namespace, http://example.com/test which shall be bound to the prefix test.

The original SVG document may have no such namespace:

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

Or it might already by declared like this:

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

So I think I need to add the namespace to the svg root element like that:

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

I was careless and executed this line on each document, no matter if the namespace was already declared or not, because I thought I would just overwrite it with the same content. But now I have lots of documents which have 0, 1 or 2 of the the same namespace declarations.

What's even more confusing, if my source document has 2 or more of those identical namespace declarations, and I add 1, 2 or even more afterwards, in the end the total sum of identical namespace declarations is always 2, never more than that.

This behavior is identical across Firefox 92, Chrome 93 and Safari 14, so it seems like it's intended. Anyway, the Web Developer DOM Inspector in Firefox somehow gets stuck and stops updating the DOM when the second namespace declaration is added.

Here's a minimal example for the 2 + 2 = 2 case. Note that I removed the code where I actually use the namespace binding for brevity:

<!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>
1 Answers

This is because xmlns:test is not valid from the SVG NameSpace, so the one in the markup will be declared in the null namespace.

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>

Then, the one in the JS will correctly be set to the 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>

Both attribute being set from a different NameSpace, they can cohabit on the same DOM element.

Related