diff --git a/src/selection/data.js b/src/selection/data.js index aadf19ac1a30a..f31c867d23c2f 100644 --- a/src/selection/data.js +++ b/src/selection/data.js @@ -36,12 +36,14 @@ d3_selectionPrototype.data = function(value, key) { keyValue; for (i = -1; ++i < n;) { - if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) { - exitNodes[i] = node; // duplicate selection key - } else { - nodeByKeyValue.set(keyValue, node); + if (node = group[i]) { + if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) { + exitNodes[i] = node; // duplicate selection key + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues[i] = keyValue; } - keyValues[i] = keyValue; } for (i = -1; ++i < m;) { @@ -55,7 +57,7 @@ d3_selectionPrototype.data = function(value, key) { } for (i = -1; ++i < n;) { - if (nodeByKeyValue.get(keyValues[i]) !== true) { + if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) { exitNodes[i] = group[i]; } } diff --git a/test/selection/data-test.js b/test/selection/data-test.js index c607ed6c1566c..e1c25b0be7307 100644 --- a/test/selection/data-test.js +++ b/test/selection/data-test.js @@ -252,6 +252,40 @@ suite.addBatch({ assert.domEqual(exit[1][0], span[1][0]); assert.domNull(exit[1][1]); }, + "does not evaluate the key function on null nodes": function(span) { + var node = span[0][0]; + span[0][0] = null; + + var update = span.data([1, 2], Number); + assert.isFalse(update.empty()); + assert.equal(update.length, 2); + assert.equal(update[0].length, 2); + assert.equal(update[1].length, 2); + assert.domEqual(update[0][0], span[0][1]); + assert.domNull(update[0][1]); + assert.domEqual(update[1][0], span[1][1]); + assert.domNull(update[1][1]); + + var enter = update.enter(); + assert.equal(enter.length, 2); + assert.equal(enter[0].length, 2); + assert.equal(enter[1].length, 2); + assert.domNull(enter[0][0]); + assert.deepEqual(enter[0][1], {__data__: 2}); + assert.domNull(enter[1][0]); + assert.deepEqual(enter[1][1], {__data__: 2}); + + var exit = update.exit(); + assert.equal(exit.length, 2); + assert.equal(exit[0].length, 2); + assert.equal(exit[1].length, 2); + assert.domNull(exit[0][0]); + assert.domNull(exit[0][1]); + assert.domEqual(exit[1][0], span[1][0]); + assert.domNull(exit[1][1]); + + span[0][0] = node; + }, "handles keys that are in the default object's prototype chain": function(span) { // This also applies to the non-standard "watch" and "unwatch" in Mozilla Firefox. var update = span.data(["hasOwnProperty", "isPrototypeOf", "toLocaleString", "toString", "valueOf"], String);