背景:我正在尝试解决将现有本地SVG
文件附加到d3
桌面应用程序中的Electron
SVG容器中的问题。我发现我无法在本地文件上使用d3.svg()
,因为fetch
不能与file
协议配合使用(所以说错误msg)。
我遇到了gist引用的this question来扩展d3.selection
的情况,它似乎可以完全满足我的需求,并添加了appendHTML
和appendSVG
函数。
尽管我测试了代码(在下面的底部),但它抛出了一个错误:“无法读取属性'未定义的原型” –在此行窒息:
d3.selection.enter.prototype.appendHTML
我通过登录控制台进行了一些挖掘,并提出了此更改,它似乎可以工作:
d3.selection.prototype.enter.prototype.appendHTML
我的问题:我这样做对吗? d3
中是否发生了某些更改,因此需要附加的prototype
参考?我不是Javascript或d3英雄,我想了解这里的区别。
d3.selection.prototype.appendHTML =
d3.selection.prototype.enter.prototype.appendHTML = function (HTMLString) {
return this.select(function () {
return this.appendChild(document.importNode(new DOMParser().parseFromString(HTMLString, 'text/html').body.childNodes[0], true));
});
};
原始代码
d3.selection.prototype.appendHTML =
d3.selection.enter.prototype.appendHTML = function(HTMLString) {
return this.select(function() {
return this.appendChild(document.importNode(new DOMParser().parseFromString(HTMLString, 'text/html').body.childNodes[0], true));
});
};
d3.selection.prototype.appendSVG =
d3.selection.enter.prototype.appendSVG = function(SVGString) {
return this.select(function() {
return this.appendChild(document.importNode(new DOMParser()
.parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + SVGString + '</svg>', 'application/xml').documentElement.firstChild, true));
});
};
答案 0 :(得分:2)
您提到的答案是使用D3 v3,但从v3到v4 / v5的情况已经发生了很大变化。关于选择的主要区别在于changelog中只有一句话:
选择不再使用原型链注入子数组;它们现在是普通对象,可以提高性能。
尽管这听起来很简单,但需要在引擎盖下进行大量更改。现在,所有选择对象都是Selection
函数的实例,该函数没有直接公开。 d3.selection
是一个返回Selection
新实例的函数:
function selection() {
return new Selection([[document.documentElement]], root);
}
尽管Selection
和d3.selection
共享相同的prototype,其中包含.enter
属性,但是除非创建了实例,否则没有属性.enter
,因此您的代码中的错误。
在v4 / v5中扩展D3的选择对象的正确方法如下:
d3.selection
.prototype // This prototype is shared across all types of selections.
.appendHTML = // Apply changes to the selection's prototype.
由于prototype
和Selection
的{{1}}属性指向同一个对象,因此这些更改将同时影响正常选择和输入选择,因为它们都是{{1 }}功能。
如您所见,这只是您自己的代码的第一行,非常好。您使用d3.selection
的扩展名只能起到某种作用:既无害也无益!在Selection
函数上设置属性是没有意义的,因为从来没有从该函数创建实例。
看看下面的工作演示,我是从您在问题中链接到的要点采纳的:
d3.selection.prototype.enter.prototype.appendHTML
.enter
d3.selection.prototype.appendHTML =
function(HTMLString) {
return this.select(function() {
return this.appendChild(
document.importNode(
new DOMParser().parseFromString(HTMLString, 'text/html').body.childNodes[0], true)
);
});
};
d3.selection.prototype.appendSVG =
function(SVGString) {
return this.select(function() {
return this.appendChild(
document.importNode(
new DOMParser()
.parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + SVGString + '</svg>', 'application/xml').documentElement.firstChild, true));
});
};
d3.select('.container').appendHTML('<svg><g><rect width="50" height="50" /></g></svg>');
var svg = d3.select('.container')
.appendHTML('<svg xmlns="http://www.w3.org/2000/svg"><g><circle class="circle1" cx="50" cy="50" r="50"></circle></g></svg>')
.select('g');
svg.appendSVG('<circle class="circle2" cx="20" cy="20" r="20"></circle>');
svg.appendSVG('<rect width="30" height="30"></rect>');