简体   繁体   中英

Is exit selection necessary in D3?

I created this simple example using data join in D3. It works: when user clicks on +1 a green is added to the list, when clicks on -1 the last color in the list is removed.

Here the code:

 const colors = ["white", "yellow", "red", "brown", "orange"]; const root = d3.select("#root"); const addColor = d3.select("#add-color"); addColor.on("click", (d) => { colors.push("green"); update(); }); const removeColor = d3.select("#remove-color"); removeColor.on("click", (d) => { colors.pop(); update(); }); const ul = root.append("ul"); ul.selectAll("li").data(colors).join( (enter) => enter.append("li").text((d) => d), (update) => update, (exit) => exit.remove() ); function update(){ ul.selectAll("li").data(colors).join( (enter) => enter.append("li").text((d) => d), (update) => update, (exit) => exit.remove() ); }
 #buttons-container { display: flex; margin-bottom: 30px; } #buttons-container div { min-width: 30px; text-align: center; cursor: pointer; border: 1px solid black; margin-right: 50px; }
 <:DOCTYPE html> <meta charset="utf-8" /> <html> <body> <div id="root"> <div id="buttons-container"> <div id="add-color">+1</div> <div id="remove-color">-1</div> </div> </root> <script type="text/javascript" src="https.//cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js" ></script> <script type="text/javascript" src="./index.js"></script> <script type="text/css" src="./styles.css"></script> </body> </html>

Then I commented the exit action:

function update() {
  ul.selectAll("li")
    .data(flavors, (d) => d)
    .join(
      (enter) =>
        enter
          .append("li")
          .text((d) => d)
          .style("color", "green"),
      (update) => update.style("color", "steelblue"),
      //(exit) => exit.style("color", "red").remove()
    );
}

And it works in the same way. What am I missing? Is exit necessary?

By default, .join calls selection.remove() for the exiting selection, without having to explicitly pass a function. The code is equivalent.

The join method is a recent addition to d3 that manages the full update pattern (enter, update, exit).

It can be used in the most simple way, without specifying any part of the pattern, eg,

 ul.selectAll("li")
    .data(flavors, (d) => d)
    .join("li")
          .text((d) => d)
          .style("color", "green");

In short, this code above will deal with the append part, will update existing elements and will remove any not needed. Its equivalent to:

var list = ul.selectAll("li")
    .data(flavors, (d) => d);

list.enter()
    .append("li")
    .merge("list")
    .text((d) => d)
    .style("color", "green");

list.exit().remove();

if we want to add specific actions for a part of the pattern, than we can specify each one.

In your case, if you only want to differentiate the colors of the enter and update, then you don't need to specify the exit part.

Just to add to the already existing answers, your question's title is a bit misleading. Let's see your title and some variants:

  • "Is [the] exit selection necessary in D3?"

    No, it's not. You can perfectly have a D3 code without an exit selection. Actually, you can even have an exit selection without calling selection.remove() , that is, you can do another stuff with the exit selection other than just removing the elements (for instance, setting a different colour). That brings us to another question:

  • "Is [the] exit selection synonymous with 'elements to be removed'?"

    No, it's not.

  • "Is [the] exit selection necessary in a dynamic data visualisation?"

    Generally yes, but again that's not necessary . So the adequate answer is no .

  • "Is [the] exit selection necessary in selection.join() ?"

    I believe that this is the question you're asking. Well, the exit selection is already there, as the other answers pointed. Here's a brief explanation:

If you look at the source code , which is quite small, you'll see:

export default function(onenter, onupdate, onexit) {
  var enter = this.enter(), update = this, exit = this.exit();
  enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
  if (onupdate != null) update = onupdate(update);
  if (onexit == null) exit.remove(); else onexit(exit);
  return enter && update ? enter.merge(update).order() : update;
}

Thus, as you can see in this line...

if (onexit == null) exit.remove(); else onexit(exit);

...if you explicitly set an exit function (here the parameter named onexit ) the code will call it, otherwise it will simply do exit.remove() , which is actually this.exit.remove() , where this is the selection.

Finally it's interesting to see that, as a convenient method, selection.join() assumes that the users want to remove the elements in the exit selection. However, as I already explained, that's not necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM