D3:附加选择的副本

Lon*_*Rob 11 svg d3.js

我想创建一个javascript函数,它可以选择一般的D3,并将它的副本附加到SVG对象.

这是一个最小的工作示例:

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

svg = d3.select("body").append("svg")
                         .attr("width", 300)
                         .attr("height", 300);

circle = svg.append("circle")
              .attr("cx", 100)
              .attr("cy", 100)
              .attr("r", 20)

function clone_selection(x, i) {
  for (j = 0; j < i; j++) {
    // Pseudo code:
    // svg.append(an exact copy of x, with all the attributes)
  }
}

clone_selection(circle, 5);
</script>
Run Code Online (Sandbox Code Playgroud)

迈克博斯托克说,这在这里是不可能的,但那是一段时间了.

有没有人对如何实现这一点有任何新的想法?请记住,在函数内部clone_selection我们不知道x中的svg元素是什么.

Lon*_*Rob 11

这是另一种可能性:做很多事情.这解决了使用<use>无法单独设置styletransform属性的元素的问题.

我很惊讶这个神奇的d3js图书馆本身没有这样的东西,但这是我的黑客:

function clone_d3_selection(selection, i) {
            // Assume the selection contains only one object, or just work
            // on the first object. 'i' is an index to add to the id of the
            // newly cloned DOM element.
    var attr = selection.node().attributes;
    var length = attr.length;
    var node_name = selection.property("nodeName");
    var parent = d3.select(selection.node().parentNode);
    var cloned = parent.append(node_name)
                 .attr("id", selection.attr("id") + i);
    for (var j = 0; j < length; j++) { // Iterate on attributes and skip on "id"
        if (attr[j].nodeName == "id") continue;
        cloned.attr(attr[j].name,attr[j].value);
    }
    return cloned;
}
Run Code Online (Sandbox Code Playgroud)


Lon*_*Rob 5

感谢@nrabinowitz为我指出<use>元素。

这是MWE的完整解决方案:

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

svg = d3.select("body").append("svg")
             .attr("width", 300)
             .attr("height", 300);

circle = svg.append("circle")
          .attr("id", "circleToClone")
          .attr("cx", 100)
          .attr("cy", 100)
          .attr("r", 20)

function clone_selection(object, i) {
  for (j = 0; j < i; j++) {
    // Here's the solution:
    cloned_obj = svg.append("use")
                .attr("xlink:href","#" + object.attr("id"));
  }
}

clone_selection(circle, 5);
</script>
Run Code Online (Sandbox Code Playgroud)

  • 就我所知,这也是更好的答案。@LondonRob,至于无法覆盖fill,cx,cy等,我发现如果原始svg定义了其中任何一个,则无法覆盖它。但是,如果您有意将其保留为空白,则可以放置所需的任何属性。 (2认同)

eag*_*gor 5

此函数对d3的选择进行深层复制,并返回复制元素的选择:

function cloneSelection(appendTo, toCopy, times) {
  toCopy.each(function() {
    for (var i = 0; i < times; i++) {
        var clone = svg.node().appendChild(this.cloneNode(true));
        d3.select(clone).attr("class", "clone");
    }
  });
  return appendTo.selectAll('.clone');
}
Run Code Online (Sandbox Code Playgroud)

在此处查看演示。

如果选择toCopy包含多个元素,则此功能也起作用。

但是请注意,如果您直接在其他地方引用内部元素,它将复制所有内容以及所有内部元素的类,id和其他属性,这可能会破坏您的代码。因此,请注意您的选择。有一个父母,可以将克隆与原始克隆区分开来,并在选择链中提及它可以确保您的安全。

合理的做法(如果确实需要大量ID)是仅在要复制的内容的外部元素上设置ID,您可以在其中通过修改函数轻松地更改ID :d3.select(clone).attr("class", "clone").attr("id", "clone-" + i)