This works:
// A
d3.select("body").selectAll(".testDiv")
.data(["div1", "div2", "div3"])
.enter().append("div")
.classed("testDiv", true)
.text(function(d) { return d; });
The following snippet is identical except that the argument for append, instead of being "div" as above, is a function(d) that simply returns "div":
// B
d3.select("body").selectAll(".testDiv")
.data(["div1", "div2", "div3"])
.enter().append(function(d) { return "div"; })
.classed("testDiv", true)
.text(function(d) { return d; });
B, however, does not work, and instead returns the error message "Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'."
How is "div" as an argument for append() different from function(d) { return "div"; }
?
The short answer is if you are giving append
a function as an argument the function must return a DOM element. The documentation of the append method states:
The name may be specified either as a constant string or as a function that returns the DOM element to append.
The following is a valid use of append with a function as an argument:
.append(function() { return document.createElement('div');});
As the code below does not return a DOM element it would be considered invalid.
.append(function() { return 'div';});
The reason for this may be seen in the source code:
d3_selectionPrototype.append = function(name) {
name = d3_selection_creator(name);
return this.select(function() {
return this.appendChild(name.apply(this, arguments));
});
};
function d3_selection_creator(name) {
function create() {
var document = this.ownerDocument, namespace = this.namespaceURI;
return namespace ? document.createElementNS(namespace, name) : document.createElement(name);
}
function createNS() {
return this.ownerDocument.createElementNS(name.space, name.local);
}
return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create;
}
As you can see if typeof name === "function"
(near the bottom) is true the create
or createNS
functions are never called. As appendChild
only accepts a DOM element the function given to append must be a DOM element.
If the purpose for adding a function within the .append()
is to have a conditional statement, then you have to make sure to have a return for every case.
You can't choose to append a div
only sometimes. A DOM element has to always be returned otherwise you will get the same error.
For example this will fail:
.append(function(d) {
if(sometimesTrue) {
return document.createElement('div');
}
});
An else {}
is required to return some other DOM element for the .append()
to work properly.
This is something I saw with D3.v3 so not sure if handled better in newer versions. Hope that helps someone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With