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

标签 javascript d3.js prototype

背景: 我试图解决将现有本地 SVG 文件附加到 Electron 中的 d3 SVG 容器的问题 桌面应用程序。我发现我不能在本地文件上使用 d3.svg() 因为 fetch 不适用于 file 协议(protocol)(所以说错误消息)。

我遇到了这个 gistthis question 引用用于扩展 d3.selection 并且它似乎完全符合我的需要,添加了 appendHTMLappendSVG 函数。

当我测试代码时(在下方底部),它抛出了一个错误:“无法读取属性‘未定义的原型(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);
}

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

由于 Selectiond3.selectionprototype 属性指向同一个对象,因此这些更改将影响 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/

相关文章:

javascript - 从日期编号创建一系列日期名称?

javascript - 为什么 function(){}.__proto__ === Function.prototype 和 Function.prototype === function(){}.__proto_ 返回不同的结果

javascript - 柯里化(Currying)函数构造函数

javascript - 为什么 Object.getPrototypeOf(instance.constructor) 不等于 instance.constructor.prototype?

php - CakePHP 中的 JavaScript

javascript - 如何确保代码在 window.location.href 之前运行

javascript - 文件下载和客户端断开连接?

javascript - D3 强制布局 : Straight line instead of curve for links (but only for some links)

javascript - 如何在 d3 图表中强制使用特定数量的 y 轴刻度?

javascript - D3 : Render Flare Graph Titles avoiding text overlap