为什么在 svg 中设置 xlink href 时 setAttribute/getAttribute 与参数不一致?

Why setAttribute/getAttribute is inconsistent with arguments when setting xlink href in svg?

为什么我们需要使用

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

在图像上设置 href 时,但紧随其后

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

returns 无效?

相反,只能使用

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

为什么在设置的时候要使用多余的"xlink:"前缀,而在获取属性值的时候却必须省略?

您似乎对命名空间元素和属性的工作方式感到困惑。

在内联标记中,您确实会向根元素添加一个 xmlns 属性,或者在您之后向命名空间属性(例如 xlink:)添加一个命名空间 header已将其 namespaceURI 定义为根元素的属性。
这是浏览器能够理解这些是命名空间所必需的,否则,它将尝试使用文档的命名空间 URI 来解析它(默认情况下,"http://www.w3.org/1999/xhtml" in html 文档)

但是,因为你处理的是javascript,这些namespaceURI需要在​​创建元素或属性时设置。
为此,您使用 document.createElementNS(namespaceURI, elementName), and element.setAttributeNS(namespaceURI, attrName, attrValue),甚至 document.createAttributeNS(...)

因为你已经在 js 中设置了它,并且浏览器不需要在标记中实际写入它,所以你不会看到 xmlns 属性,也不会看到 xlink: NSheader 在你的检查器中,但他们在这里。

您可以调用元素的 outerHTML 属性 来说服自己,顺便说一句,获得 DOM 的字符串表示的更好方法是使用 XMLSerializer 并且它是 serializeToString(elem) 方法。

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://whosebug.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));

getAttribute的工作方式相同:non-NS版本将使用调用它的document的命名空间。

现在,为什么你可以调用 setAttributeNS(nameSpaceURI, 'namespace:attr', val),但不能调用 getAttributeNS(nameSpaceURI, 'namespace:attr')`,这是因为在这种特殊情况下,浏览器足够友好地纠正你.

你可以看到调用 setAttribute('xlink:href', value) 将创建一个不同于 NS 版本设置的属性,并且使用 lambda header 设置 href 属性只会覆盖没有这个的属性header。在这里设置这个header的唯一原因是为了在将节点序列化为字符串时控制命名空间的名称;浏览器会有不同的默认值,但这个 xlink 名称只是一个约定,实际上可以是任何你想要的。

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);
}

但是如果你在 Chrome 上尝试这个,你可能会看到实际保留的一个属性是没有命名空间的那个。这是一个错误。