How to check if a given object is a d3
selection?
The following code prints true
in Chrome and Firefox, but false
in Internet Expl
With the release of D3 v4 this problem has vanished (changelog):
Selections no longer subclass Array using prototype chain injection; they are now plain objects, improving performance.
And the API docs explicitly state:
# d3.selection() <>
[…] This function can also be used to test for selections (
instanceof d3.selection
)
Using the new version, the following code will actually evaluate to true in all browsers:
d3.select() instanceof d3.selection // true in Chrome, FF, IE
For all those still on v3 the original answer below has an analysis and a workaround for the problem.
Due to the inner workings of D3 every browser which supports Object.prototype.__proto__ will print true
, whereas browsers lacking support for __proto__
will print false
. Checking the compatibility list it's obvious, that IE<11 will evaluate the expression to false
. For this reason, you won't be able to use instanceof d3.selection
to check for a D3 selection in IE<11. This is a known issue with D3, but it was closed and will not get fixed.
From D3's github repository:
selection/selection.js
Looking at the definition of d3.select() which is the entry point of your call:
d3.select = function(node) {
// ... removed for brevity
return d3_selection([group]);
};
This will eventually return the result of the call to d3_selection(), which in turn will subclass d3_selectionPrototype = d3.selection.prototype
.
function d3_selection(groups) {
d3_subclass(groups, d3_selectionPrototype);
return groups;
}
core/subclass.js
Finally, the implementation of d3_subclass() provides the answer to the problem:
var d3_subclass = {}.__proto__?
// Until ECMAScript supports array subclassing, prototype injection works well.
function(object, prototype) {
object.__proto__ = prototype;
}:
// And if your browser doesn't support __proto__, we'll use direct extension.
function(object, prototype) {
for (var property in prototype) object[property] = prototype[property];
};
It checks, if the browser supports Object.prototype.__proto__
by checking for the existence of the __proto__
accessor property on an empty object {}
. If the browser supports it, D3 will directly assign the prototype, thus making it an instance of d3.selection
. Otherwise, all properties of the prototype will be copied over to the object to be returned without explicitely setting the prototype. In this case your expression will evaluate to false
.
Because d3.selection is provided as a means to extend the selection's functionality you could implement a workaround by adding a new property to d3.selection
which will, as was explained above, be made accessible by any selection, wether by prototyping or by copying properties.
// Include this at the start of your script to include the
// property in any selection created afterwards.
d3.selection.prototype.isD3Selection = true;
console.log(d3.select(document.body).isD3Selection); // true in any browser