如何创建“svg”对象而不附加它?

时间:2013-08-27 00:54:38

标签: javascript d3.js

请考虑以下代码:

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。这反过来又减少了问题:如何在不使用append的情况下实例化“svg”对象,因为可以执行以下操作:

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

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

6 个答案:

答案 0 :(得分:29)

您可以使用以下内容:

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'); 元素,然后在该单个元素上创建一个选择。

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

{{1}}

答案 1 :(得分:24)

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

var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');

答案 2 :(得分:6)

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

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

答案 3 :(得分:6)

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

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

  

# d3。创建(姓名)<>

     

给定指定的元素 name ,返回单元素选择   在当前包含给定名称的分离元素   文档。

这个新功能可以按如下方式使用:

// 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注册的任何namespace创建元素。

答案 4 :(得分:2)

D3作者Mike Bostock在他的comment on an old D3 Github "issue"中提出了另一个(更简单的)方法,询问了这个话题:

  

您可以考虑的另一个策略是在创建DOM后立即从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它在操作和创建子元素之前就已经存在,因此不应该重复浏览器。虽然技术上与原始问题相反,但这可能是满足要求的最惯用的方式。

答案 5 :(得分:1)

我正在使用4.4.4版

var svg = document.createElementNS(d3.namespaces.svg, "svg");