Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
166 views
in Technique[技术] by (71.8m points)

d3.js - How can I merge or join d3 selections from different d3.selects?

In my code I have a problem with merging selections. The condensed code is as follows.

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://d3js.org/d3.v6.min.js"></script>
  </head>
  <body>
    <svg>
      <g id="g1" class="testg"><text x="20" y="20">bla1</text></g>
    </svg>
    <svg>
      <g id="g2" class="testg"><text x="20" y="20">bla2</text></g>
    </svg>

    <script>
      let s0 = d3.selectAll("g");
      s0.style("fill", "red"); // works, all are red;

      let s1 = d3.select("g#g1");
      let s2 = d3.select("g#g2");

      s1.style("fill", "green"); //works g1 green
      s2.style("fill", "yellow"); //works g2 yellow

      let s3 = s1.merge(s2);
      s3.style("fill", "pink"); // does not work: only g1 is pink
    </script>
  </body>
</html>

I would expect this to work, but it doesn't. How can I merge s1 and s2 into one selection? In my live code, they are not easily selectable by attributes.

Edit: Of course I can work around the problem like this:

  s1.classed("select_for_merge", true);
  s2.classed("select_for_merge", true);
  s3 = d3.selectAll("g.select_for_merge");
  s3.classed("select_for_merge", false);

It just seems unwieldy, considering I already have multiple selections, only from different parts of the application. Giving them all extra classes is unwieldy and hampers performance, in the live code it consists of many elements in 4 dimensions.

question from:https://stackoverflow.com/questions/66065895/how-can-i-merge-or-join-d3-selections-from-different-d3-selects

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The behavior you witnessed is expected and it is well documented:

# selection.merge(other) · Source

This method is not intended for concatenating arbitrary selections, however: if both this selection and the specified other selection have (non-null) elements at the same index, this selection’s element is returned in the merge and the other selection’s element is ignored.

This explains why only the #g1 element is selected after the merge operation.

Although there are multiple ways doing this, my preferred and, maybe the most elegant way, was made possible with the release of D3 v6. Selections now implement an iterator and are thus iterable themselves making them accessible for many language feature introduced by ES6! In general, concatenating arrays can easily be implemented using spread syntax:

const concatArray = [...array1, array2];

Since selection are now iterable you can concatenate their respective nodes the same way:

const concatNodes = [...selection1, ...selection2];

Apart from being iterable, selections can also be created by passing in an iterable object which results in the following code for merging two selections:

const mergedSelection = d3.selectAll([...selection1, ...selection2]);

Given your example this can be implemented as follows:

const s0 = d3.selectAll("g");
s0.style("fill", "red"); // works, all are red;

const s1 = d3.select("g#g1");

const s2 = d3.select("g#g2");

const s3 = d3.selectAll([...s1, ...s2]);
s3.style("fill", "blue"); // works, all are blue
<script src="https://d3js.org/d3.v6.js"></script>
<svg>
  <g id="g1" class="testg"><text x="20" y="20">bla1</text></g>
</svg>
<svg>
  <g id="g2" class="testg"><text x="20" y="20">bla2</text></g>
</svg>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...