36

考虑以下代码:

var svg = d3.select('#somediv').append("svg").attr("width", w).attr("height", h);

我想重构这段代码,使它看起来更像这样:

var svg = makesvg(w, h);
d3.select("#somediv").append(svg);

请注意,与第一个版本中显示的情况相反,在第二个版本append 中不创建“svg”对象;它仅将其附加到d3.select("#somediv").

问题是如何实现该功能makesvg。这反过来又减少了问题:如何在不使用的情况下实例化一个“svg”对象append,因为然后可以执行以下操作:

function makesvg(width, height) {
  return _makesvg().attr("width", w).attr("height", h);
}

所以我的问题归结为_makesvg()上面提到的假设工厂的通用等价物是什么?

4

6 回答 6

31

您可以使用以下内容:

var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');

注意使用createElementNS. 这是必需的,因为svg元素与大多数 HTML 元素不在同一个 XHTML 名称空间中。

这段代码创建一个新svg元素,无论是否使用 D3,然后在该单个元素上创建一个选择。

这可以稍微更简洁,但更清晰,更不容易出错,因为:

var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
于 2013-11-13T10:21:22.797 回答
24

为了节省一点时间,您可以使用d3.ns.prefix.svg

var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
于 2014-01-13T21:18:10.367 回答
13

最后,随着 D3 v5 ( 2018年3 月 22 日)的发布,现在可以在 D3 本身中完成。这不会影响其他答案,无论如何,这些答案仍然有效,最后,D3 将document.createElementNS()像以前的帖子中描述的那样使用。

从 v5 开始,您现在可以使用:

# d3。创建(名称)<>

给定指定的元素名称,返回一个单元素选择,其中包含当前文档中给定名称的分离元素。

此新功能可按如下方式使用:

// Create detached <svg> element.
const detachedSVG = d3.create("svg");

// Manipulate detached element.
detachedSVG
  .attr("width", 400)
  .attr("height", 200);

// Bind data. Append sub-elements (also not attached to DOM).
detachedSVG.selectAll(null)
  .data([50, 100])
  .enter()
  //...

 // Attach element to DOM.
 d3.select("body")
   .append(() => detachedSVG.node());

查看以下代码片段,了解创建分离子树的工作演示,该子树附加在点击事件上:

// Create detached <svg> element.
const detachedSVG = d3.create("svg");

// Manipulate detached element.
detachedSVG
  .attr("width", 400)
  .attr("height", 200);

// Bind data. Attach sub-elements.
detachedSVG.selectAll(null)
  .data([50, 100])
  .enter().append("circle")
    .attr("r", 20)
    .attr("cx", d => d)
    .attr("cy", 50);

// Still detached. Attach on click.
d3.select(document)
  .on("click", () => {
     // Attach element to DOM.
     d3.select("body")
       .append(() => detachedSVG.node());
  });
<script src="https://d3js.org/d3.v5.js"></script>

这种方法的主要优点是易于使用和清晰。元素的创建是由 D3 在幕后完成的,它可以作为一个完整的选择提供,并提供所有方法。除了操作新创建的分离元素之外,返回的选择还可以很容易地用于数据绑定。

还值得注意的是,该函数不限于从 SVG 命名空间创建元素,而是可用于从任何向 D3 注册的命名空间创建元素。

于 2018-04-01T20:00:04.210 回答
8

这是一个创建未附加组元素的示例函数:

function createSomething(){
  return function(){
    var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
    // Add stuff...
    return group.node();
  }
}

你可以这样称呼它:

node.append(createSomething());

解释

假设您正在渲染一个可折叠的树,并且您希望将带有圆形边框的加号/减号图标作为切换。您的绘图函数已经非常庞大,因此您希望将用于绘制加号的代码放入它自己的函数中。绘制/更新方法将负责正确定位。

一种选择是将现有容器传递给函数:

createPlus(node).attr({
  x: 10,
  y: 10
});

function createPlus(node){
  var group = node.append('g');
  // Add stuff...
  return group;
}

我们可以通过应用@Drew 和@Paul 的技术来创建未附加的元素来使这一点变得更好。

node.append(createPlus())
    .attr({
      x: 10,
      y: 10
    });

function createPlus(){
  var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
  // Add stuff...
  return group;
}

除了抛出错误,因为append()需要一个字符串或一个函数。

名称可以指定为常量字符串,也可以指定为返回要追加的 DOM 元素的函数。

所以我们只需将其更改为:

node.append(function(){
  return createPlus();
});

但这仍然行不通。它会导致以下错误:

TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.

幸运的是,我发现selection.node()哪个有效!虽然,诚然,我不知道为什么。

function createPlus(){
  var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
  // Add stuff...
  return group.node();
}

我们可以通过将匿名函数移动到createPlus

node.append(createPlus())

function createPlus(){
  return function(){
    var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
    // Add stuff...
    return group.node();
  }
}
于 2015-05-29T20:09:05.253 回答
3

D3 作者 Mike Bostock 在他对旧的 D3 Github“问题”的评论中提出了另一种(更简单)的方法,该问题询问了这个主题:

您可以考虑的另一种策略是在创建元素后立即从 DOM 中删除它:

var svg = d3.select("body").append("svg")
    .remove()
    .attr("width", w)
    .attr("height", w);

svg.append("circle")
    .attr("r", 200);

document.body.appendChild(svg.node());

这种方法确实在创建时附加元素,但.remove它是在操作和创建子元素之前立即添加的,因此不应该有浏览器重绘。虽然在技术上与原始问题相反,但这可能是满足要求的最惯用的方式。

于 2016-12-15T20:39:21.770 回答
1

我正在使用版本 4.4.4

var svg = document.createElementNS(d3.namespaces.svg, "svg");
于 2017-01-25T15:47:56.337 回答