0

Why do we need to use

element.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "link")

when setting href on image, but right after that

element.getAttributeNS("http://www.w3.org/1999/xlink", "xlink:href")

returns null?

Instead, attribute can only be retrieved using

element.getAttributeNS("http://www.w3.org/1999/xlink", "href")

Why should we use redundant "xlink:" prefix when setting, but must omit it when getting the attribute value?

6
  • as a getter you don't even need getAttributeNS ; getAttribute should work just fine; Commented Jun 10, 2016 at 10:51
  • @maioman - that's true, but then what is the purpose of getAttributeNS at all? Commented Jun 10, 2016 at 10:54
  • Why? because the various specifications say so. I'm not sure what kind of answer would satisfy you here. Commented Jun 10, 2016 at 11:54
  • @RobertLongson - can you give a link to specification that defines such behavior? Commented Jun 10, 2016 at 13:30
  • You should not set redundant xlink prefix, even when setting the attribute. If you use the setAttributeNS, the namespace will be added to the attribute. The prefix is only for inline attributes. Commented Jun 11, 2016 at 2:40

1 Answer 1

4

You seem to be confused on how the namespaced elements and attributes do work.

In your inlined markup, you'll indeed add either an xmlns attribute to the root element, or an namespace header to your namespaced attributes (e.g xlink:) after you've defined its namespaceURI as an attribute on the root element.
This is necessary for the browser to be able to understand that these are namespaced, otherwise, it will try to parse it with the document's namespaceURI (by default, "http://www.w3.org/1999/xhtml" in html document)

But, since you're dealing with javascript, these namespaceURIs need to be set at creation of the element, or of the attribute.
For this, you use document.createElementNS(namespaceURI, elementName), and element.setAttributeNS(namespaceURI, attrName, attrValue), or even document.createAttributeNS(...).

Because you've set it in js, and that the browser doesn't need to actually write it in the markup, you'll not see the xmlns attribute nor xlink: NSheader in your inspector, but they're here.

You can just call the outerHTML property of your element to convince yourself, and btw, a better way to get a string representation of the DOM is to use an XMLSerializer and it's serializeToString(elem) method.

console.log("document's namespaceURI :",document.documentElement.namespaceURI)
// we're in an html document, 
//  this means that all 'createElement(xxx)' will correspond to
//  'document.createElementNS('http://www.w3.org/1999/xhtml', xxx)'


// our two namespace URIs
var svgNS = 'http://www.w3.org/2000/svg';
var xlinkNS = 'http://www.w3.org/1999/xlink';

// There is also an HTMLSVGElement 
  //but since you seem to want to make a standaone svg document from this node, 
  // you have to use the NS version
var svgRoot = document.createElementNS(svgNS, 'svg');

var a = document.createElementNS(svgNS, 'a');

// this is an xlink attribute
// note that you should not set the 'xlink:' header, the browser already know its namespace
a.setAttributeNS(xlinkNS, 'href', 'http://stackoverflow.com');

// an other SVG element
var r = document.createElementNS(svgNS, 'rect');
r.setAttribute('width', 50);
r.setAttribute('height', 50);

a.appendChild(r);
svgRoot.appendChild(a);
document.body.appendChild(svgRoot);
// here we see that the 'xlink:' header is actually set,  
 // but it forgets the namespace URI declarations in some UAs (at least in FF...).
console.log("outerHTML :", svgRoot.outerHTML);
// so better use an XMLSerializer :
console.log("serialized :", new XMLSerializer().serializeToString(svgRoot));

And getAttribute works the same way : non-NS version will use the namespace of the document calling it.

Now, for the why you can call setAttributeNS(nameSpaceURI, 'namespace:attr', val), but can't call getAttributeNS(nameSpaceURI, 'namespace:attr')`, it's because browsers are kind enough to correct you, in this particular case.

You can see that calling setAttribute('xlink:href', value) will create an other attribute than the one set by the NS version, and that setting an href attribute with a lambda header will just overwrite the one without this header. The only reason to set this header here is to control the name of the namespace when serializing the node to a string ; browsers will have different default values, but this xlink name is just a convention, it can actually be whatever you want.

var svgNS = 'http://www.w3.org/2000/svg';
var xlinkNS = 'http://www.w3.org/1999/xlink';

var svgRoot = document.createElementNS(svgNS, 'svg');
var a = document.createElementNS(svgNS, 'a');

// browsers should keep it but don't set it as the actual link target
a.setAttribute('xlink:href', 'http://noHeader.com');
// same here
a.setAttribute('href', 'http://noNameSpace.com');
// this one is correct
a.setAttributeNS(xlinkNS, 'href', 'http://xlinkNoHeader.com');
// but this will overwrite the previous one without the header
a.setAttributeNS(xlinkNS, 'other:href', 'http://xlinkOtherHeader.com');

var r = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
r.setAttribute('width', 50);
r.setAttribute('height', 50);
a.appendChild(r);
svgRoot.appendChild(a);
document.body.appendChild(svgRoot);

for (var i = 0; i < a.attributes.length; i++) {
  console.log(a.attributes[i].name, a.attributes[i].textContent, a.attributes[i].namespaceURI);
}

But if you try this on Chrome, you may see that the one attribute actually retained is the one without namespace. This is a bug.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.