enter().insert(...) doesn't preserve order
Epiphero opened this issue · comments
The d3 semantics of enter().insert(...) are such that when performing an insert on the enter selection of a join with a key, the "entering elements [should] be inserted immediately before the next following sibling in the update selection, if any."
However, when using d3-jetpack, the entering elements are merely appended to the end.
The following is a toy example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Insert Order Bug</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
<script src="https://raw.githubusercontent.com/gka/d3-jetpack/master/d3-jetpack.js"></script>
</head>
<body>
<script type="text/javascript">
var identity = function (x) { return x; };
// Make a list of odd numbers less than 10
d3
.select("body")
.selectAll("div")
.data([1, 3, 5, 7, 9], identity)
.enter()
.insert("div")
.html(identity)
;
// Change the list to prime numbers less than 10
var update = d3
.select("body")
.selectAll("div")
.data([2, 3, 5, 7], identity)
;
// Color the update selection yellow
update.style("background-color", "yellow");
// Insert and color the enter selection green
update
.enter()
.insert("div")
.html(identity)
.style("background-color", "green")
;
// Remove those pesky composite numbers
update
.exit()
.remove()
;
</script>
</body>
</html>
This page will show the list "3, 5, 7, 2", however commenting out the d3-jetpack script will preserve the d3 semantics and produce the list "2, 3, 5, 7".
And a very quick & dirty patch to fix:
--- d3-jetpack.js.orig 2017-02-08 16:49:07.000000000 -0600
+++ d3-jetpack.js 2017-02-08 16:47:26.000000000 -0600
@@ -57,8 +57,21 @@
//no selection.enter in v4
if (d3.selection.enter){
- d3.selection.enter.prototype.append = d3.selection.prototype.append
- d3.selection.enter.prototype.insert = d3.selection.prototype.insert
+ function d3_selection_enterInsertBefore(enter) {
+ var i0, j0;
+ return function(d, i, j) {
+ var group = enter[j].update, n = group.length, node;
+ if (j != j0) j0 = j, i0 = 0;
+ if (i >= i0) i0 = i + 1;
+ while (!(node = group[i0]) && ++i0 < n) ;
+ return node;
+ };
+ }
+ d3.selection.enter.prototype.append = d3.selection.prototype.append;
+ d3.selection.enter.prototype.insert = function(name, before) {
+ if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
+ return d3.selection.prototype.insert.call(this, name, before);
+ };
}
var d3_parse_attributes_regex = /([\.#])/g;
And as I continue to dig, this only applied to d3.v3, d3.v4's enter().append(...) takes over the former enter().insert(...) semantics, and d3-jetpack works fine with that.
Weird! We are only supporting v4 now