JSFiddle - React, Tailwind, and code Playground
HTML
<h1><pre></pre></h1>
<p>Even with zero circles, <strong>d3.transition.end</strong> will apply its <strong>callback</strong> after <strong>delayIfEmpty</strong> milliseconds. Passing falsey second argument will skip it, and the value of <strong>0</strong> will apply it without delay.</p>
<input type="number" min="0" value="3" onclick="showCircles(this.value)">
CSS
html, body, svg {
margin: 0;
width: 100%;
height: 100%;
background: gold;
text-align: center;
}
circle {
fill: olive;
}
input[type="number"] {
outline: none;
font: bold 2em monospace;
background: gold;
border: none;
width: 2em;
}
p {
font-family: monospace;
width: 50%;
margin-left: auto;
margin-right: auto;
}
JavaScript
// re: http://stackoverflow.com/a/20773846/1409907
function drop(n, args, callback) {
for (var i = 0; i < args.length - n; ++i) args[i] = args[i + n];
args.length = args.length - n;
callback.apply(this, args);
}
d3.transition.prototype.end = function(callback, delayIfEmpty) {
var f = callback,
delay = delayIfEmpty,
transition = this;
drop(2, arguments, function() {
var args = arguments;
if (!transition.size() && (delay || delay === 0)) { // if empty
d3.timer(function() {
f.apply(transition, args);
return true;
}, typeof(delay) === "number" ? delay : 0);
} else { // else Mike Bostock's rutine
var n = 0;
transition.each(function() { ++n; })
.each("end", function() {
if (!--n) f.apply(transition, args);
});
}
});
return transition;
}
function showCircles(testSize) {
var msg = d3.select("pre").text("...");
d3.selectAll("svg").remove();
var svg = d3.select("body").append("svg");
var circles = svg.selectAll("circle")
.data(d3.range(testSize))
.enter()
.append("circle")
.attr({ r: 10, cx: "20%", cy: function(d,i){return i * 25 + 50;}});
var callCount = 0;
circles.transition()
.delay(500)
.duration(1500)
.attr("cx", "80%")
.end(function(x) {
console.log(arguments);
msg.text("all done (called " + (++callCount) + " times " + x +")");
}, 1500, "with args");
}
var initValue = parseInt(d3.select("input[type='number']").node().value);
showCircles(initValue);