在v5中扩展d3.selection-这是正确的方法吗?

时间:2018-10-11 05:41:30

标签: javascript d3.js prototype

背景:我正在尝试解决将现有本地SVG文件附加到d3桌面应用程序中的Electron SVG容器中的问题。我发现我无法在本地文件上使用d3.svg(),因为fetch不能与file协议配合使用(所以说错误msg)。

我遇到了gist引用的this question来扩展d3.selection的情况,它似乎可以完全满足我的需求,并添加了appendHTMLappendSVG函数。

尽管我测试了代码(在下面的底部),但它抛出了一个错误:“无法读取属性'未定义的原型” –在此行窒息:

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

1 个答案:

答案 0 :(得分:2)

您提到的答案是使用D3 v3,但从v3到v4 / v5的情况已经发生了很大变化。关于选择的主要区别在于changelog中只有一句话:

  

选择不再使用原型链注入子数组;它们现在是普通对象,可以提高性能。

尽管这听起来很简单,但需要在引擎盖下进行大量更改。现在,所有选择对象都是Selection函数的实例,该函数没有直接公开。 d3.selection是一个返回Selection新实例的函数:

function selection() {
  return new Selection([[document.documentElement]], root);
}

尽管Selectiond3.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.

由于prototypeSelection的{​​{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>');