背景: 我试图解决将现有本地 SVG
文件附加到 Electron 中的
桌面应用程序。我发现我不能在本地文件上使用 d3
SVG 容器的问题d3.svg()
因为 fetch
不适用于 file
协议(protocol)(所以说错误消息)。
我遇到了这个 gist由 this question 引用用于扩展 d3.selection
并且它似乎完全符合我的需要,添加了 appendHTML
和 appendSVG
函数。
当我测试代码时(在下方底部),它抛出了一个错误:“无法读取属性‘未定义的原型(prototype)’——这一行令人窒息:
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));
});
};
最佳答案
您提到的答案使用的是 D3 v3,但是从 v3 到 v4/v5 情况发生了很大变化。 changelog 中的一句话涵盖了选择方面的主要区别。 :
Selections no longer subclass Array using prototype chain injection; they are now plain objects, improving performance.
虽然这听起来很简单,但它仍然需要在引擎盖下进行大量更改。所有选择对象现在都是 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.
由于 Selection
和 d3.selection
的 prototype
属性指向同一个对象,因此这些更改将影响 normal 和 enter选择,因为它们都是 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>');
div,
svg {
border: 1px solid silver;
margin: 10px;
}
rect {
fill: skyblue;
}
.circle1 {
fill: orange;
}
.circle2 {
fill: lime;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div class="container"></div>
关于javascript - 在 v5 中扩展 d3.selection——这是正确的做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38151243/