Skip to content

Instantly share code, notes, and snippets.

@john-guerra
Last active September 4, 2019 19:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save john-guerra/cabfbaed34d942e4dd81199855e4987f to your computer and use it in GitHub Desktop.
Save john-guerra/cabfbaed34d942e4dd81199855e4987f to your computer and use it in GitHub Desktop.
General Update Pattern Explained
license: gpl-3.0

Explaining D3 data binding. In this example you can enter your own data to the general update pattern and see the elements that will fall on each of the three states (enter, update, exit).

forked from mbostock's block: General Update Pattern, III

Original Readme by mbostock

By adding transitions, we can more easily follow the elements as they are entered, updated and exited. Separate transitions are defined for each of the three states. To avoid repeating transition timing parameters for the enter, update, and exit selections, a top-level transition t sets the duration, and then subsequent transitions use selection.transition, passing in t to inherit timing:

var t = d3.transition()
    .duration(750);

Want to read more? Try these tutorials:

See the D3 wiki for even more resources.

Previous: Key Functions

forked from mbostock's block: General Update Pattern, III

<!DOCTYPE html>
<meta charset="utf-8">
<style>
text {
font: bold 32px monospace;
}
.set text {
font: 18px monospace;
}
.enter {
fill: green;
}
.update {
fill: #333;
}
.exit {
fill: brown;
}
input {
width: 280px;
font-size: 18pt;
}
.controlPanel {
float:left;
}
button {
font-size: 18pt;
}
</style>
<div class="controlPanel">
<h2>Enter your own data</h2>
<input id="data" type="text">
<br>
<button id="btnUpdate">Update</button>
<button id="btnRandom">Random</button>
<h3>Previous data:</h3>
<div id="previous"></div>
</div>
<svg width="580" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(32," + (height / 4) + ")"),
gGroups = svg.append("g").attr("transform", "translate(32," + (height / 4 * 3) + ")")
;
function update(data) {
var t = d3.transition()
.duration(750);
// JOIN new data with old elements.
var text = g.selectAll("text")
.data(data, function(d) { return d; });
// EXIT old elements not present in new data.
text.exit()
.attr("class", "exit")
.transition(t)
.attr("y", 60)
.style("fill-opacity", 1e-6)
.remove();
// UPDATE old elements present in new data.
text.attr("class", "update")
.attr("y", 0)
.style("fill-opacity", 1)
.transition(t)
.attr("x", function(d, i) { return i * 18; });
// ENTER new elements present in new data.
text.enter().append("text")
.attr("class", "enter")
.attr("dy", ".35em")
.attr("y", -60)
.attr("x", function(d, i) { return i * 18; })
.style("fill-opacity", 1e-6)
.text(function(d) { return d; })
.transition(t)
.attr("y", 0)
.style("fill-opacity", 1);
updateSets(data, text);
}
// Visualize the enter, update and exit sets of the passed selection
function updateSets(data, sel) {
var getData = function(sel) {
return sel.nodes().map(function (d) { return d.__data__; });
};
var set = gGroups.selectAll(".set")
.data([getData(sel.enter()), //enter set (created)
getData(sel), //update set
getData(sel.exit()) //exit set (removed)
]);
var setEnter = set.enter()
.append("g")
.attr("class", "set");
setEnter
.append("text")
.attr("class", "setTitle")
.attr("x", width/6)
.text(function (_,i) {
return i===0 ? "Enter":
i===1 ? "Update":
"Exit";
});
setEnter
.merge(set)
.classed("enter", function (_,i) { return i===0; })
.classed("update", function (_,i) { return i===1; })
.classed("exit", function (_,i) { return i===2; })
.attr("transform", function (_, i) {
return "translate(" + ( i*width/3 ) + ",0)";
});
set.exit().remove();
var text = setEnter
.merge(set)
.selectAll(".letter")
.data(function (d) { return d; });
text.enter()
.append("text")
.attr("class", "letter")
.merge(text)
.attr("y", 32)
.attr("x", function (d,i) { return i*9; })
.text(function (d) {
return d;
});
text.exit().remove();
d3.select("#data")
.property("value", data.join(","));
d3.select("#previous")
.insert("p", ":first-child")
.text(data.join(","));
}
// The initial display.
update(alphabet);
d3.select("#btnRandom")
.on("click", function () {
update(d3.shuffle(alphabet)
.slice(0, Math.floor(Math.random() * 26))
.sort());
});
d3.select("#btnUpdate")
.on("click", function () {
var data;
try {
data = d3.select("#data").property("value").split(",").map(d=> d.trim());
} catch (Exception) {
alert("Please enter a comma separated list");
}
update(data);
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment