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



为什么我们需要使用

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

在图像上设置href时,但在之后

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

返回null?

相反,只能使用检索属性

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

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

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

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

但是,因为您处理的是javascript,所以需要在创建元素或属性时设置这些namespaceURI
为此,您可以使用document.createElementNS(namespaceURI, elementName)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://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));

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

现在,对于为什么您可以调用setAttributeNS(nameSpaceURI, 'namespace:attr', val),但不能调用getAttributeNS(nameSpaceURI,'namespace:attr')`,这是因为在这种特殊情况下,浏览器足以纠正您的错误。

您可以看到,调用setAttribute('xlink:href', value)将创建一个不同于NS版本设置的属性,而设置一个带有lambda标头的href属性只会覆盖没有此标头的属性。在此处设置此标头的唯一原因是在将节点序列化为字符串时控制命名空间的名称;浏览器将有不同的默认值,但这个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上尝试这个,你可能会发现实际保留的一个属性是没有名称空间的属性。这是一个bug。

最新更新