Reputation: 438
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?
Upvotes: 3
Views: 156
Reputation: 102194
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.
Upvotes: 1
Reputation: 737
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.
Upvotes: 2