在svg中,如果我使用knockout为xlink:href
节点设置a
属性,则属性的命名空间设置不正确,因此a
不起作用点击时的链接。
例如,请考虑以下包含两个链接省略号的svg。一个属性的xlink:href
属性是硬编码的,另一个属性是通过data-bind
属性的淘汰赛设置的:
<svg width="5cm" height="6cm" viewBox="0 0 5 6" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x=".01" y=".01" width="4.98" height="5.98"
fill="none" stroke="blue" stroke-width=".03"/>
<a xlink:href="#hardcoded">
<ellipse data-bind="attr: blue" />
</a>
<a data-bind="attr: { 'xlink:href': href }">
<ellipse data-bind="attr: red" />
</a>
</svg>
让淘汰赛很简单:
ko.applyBindings( {
blue: { cx:2.5, cy:1.5, rx:2, ry:1, fill:"blue" },
href: '#foo',
red: { cx:2.5, cy:4.5, rx:2, ry:1, fill:"red" },
});
但只有硬编码的链接有效。如果我添加一些代码来查看属性节点的namespaceURI
值,我可以看到
由knockout设置的xlink:href
属性有一个null
namespaceURI,而不是硬编码的,设置为http://www.w3.org/1999/xlink
。
Array.forEach( document.getElementsByTagName('a'), function(a){
a.setAttribute('title', 'xlink:href namespaceURI = ' + a.getAttributeNode('xlink:href').namespaceURI);
});
是否有一种简单的方法可以告诉敲除属性的正确名称空间应该是什么,或者我是否需要编写自定义绑定?
答案 0 :(得分:4)
我的后备解决方案是添加此自定义绑定:
ko.bindingHandlers['attr-ns'] = {
update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.utils.objectForEach( ko.unwrap( valueAccessor() ), function(name, value){
var prefixLen = name.indexOf(':');
var prefix = name.substr( 0, prefixLen );
var namespace = prefixLen < 0 ? null : element.lookupNamespaceURI( prefix );
element.setAttributeNS( namespace, name, ko.unwrap( value ) );
});
}
};
并更改模板以使用我定义的attr-ns
绑定:
<a data-bind="attr-ns: { 'xlink:href': href }">
<ellipse data-bind="attr: red" />
</a>
这似乎工作正常,但如果我不需要,我宁愿不这样做。更多自定义代码=更多可能出错。
答案 1 :(得分:1)
您必须使用名称空间感知版本覆盖the default attr
binding handler。
FWIW,这是我对它的看法,使用它作为透明的插件,它甚至支持名称空间前缀:
var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' },
namespaceDecl = {
svg: "http://www.w3.org/2000/svg",
xlink: "http://www.w3.org/1999/xlink"
};
ko.bindingHandlers['attr'] = {
'update': function(element, valueAccessor, allBindings) {
var value = ko.utils.unwrapObservable(valueAccessor()) || {};
ko.utils.objectForEach(value, function(attrName, attrValue) {
var attrRawValue = ko.utils.unwrapObservable(attrValue),
toRemove = (attrRawValue === false) || (attrRawValue === null) || (attrRawValue === undefined),
attrStrValue = attrRawValue.toString(),
attrNameParts = attrName.split(":"),
attrNsUri = (attrNameParts.length === 2) ? namespaceDecl[attrNameParts[0]] : null,
attrName = (attrNsUri) ? attrNameParts[1] : attrNameParts[0];
if (toRemove) {
if (attrNsUri) {
element.removeAttributeNS(attrNsUri, attrName);
} else {
element.removeAttribute(attrName);
}
}
if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap && !attrNsUri) {
attrName = attrHtmlToJavascriptMap[attrName];
if (toRemove)
element.removeAttribute(attrName);
else
element[attrName] = attrValue;
} else if (!toRemove) {
if (attrNsUri) {
element.setAttributeNS(attrNsUri, attrName, attrStrValue);
} else {
element.setAttribute(attrName, attrStrValue);
}
}
if (attrName === "name") {
ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
}
});
}
};