I’m new to d3 and I need some help understanding html element reuse.
Here is a Fiddle: http://jsfiddle.net/m1erickson/qmEBE/
I put an existing paragraph element in the body:
<body>
<p> Old Text.</p>
</body>
Then I define a dataset of 3 numbers and do a .selectAll(“p”) like this:
var dataset = [10,20,30];
d3.select("body").selectAll("p")
.data(dataset)
.enter()
.append("p")
.text( function(d){ return("#"+d); } ) ;
I get this:
Old Text.
#20
#30
Two things trouble me:
Why did d3 not create a new p-tag and display the #10?
When I look at the pre-existing paragraph element (“Old Text”), d3 has assigned the d3 data attribute==10. If d3 captured this p-tag, why not use it to display the datum?
How does d3 decide which pre-existing webpage elements to include in its “update selection”?
BTW—on d3 so far: I feel liberated by the idea of defining data first and having the design follow.
[ Edited: So do I have this right? ]
Based on the informative answer from @meetamit…
The following creates an update selection inside elements
elements =d3.select(“body”).selectAll(“p”).data(dataset)
The following causes d3 to create an enter selection where p-tags are created for 20 and 30.
elements.enter() .append("p")
Question:
At this point is the update selection already merged with the enter selection?
The following sets the text in all 3 p-tags because they are all in a merged update selection.
elements.text(function(d){return("#"+d);});
Question:
Since d3 will capture existing p-tags, etc from a webpage, is it common practice to make the initial d3.select point to a container that will host the d3 results, like this:
elements = d3.select(“#myD3Div”) ….
When you call data(dataset)
and there's one element already on the page, d3 decides that this element is to be associated with 10
-- the data of the first array element. That's because that element is the 0th <p>
on the page and it therefore must be associated with the 0th element of your dataset. In other words, it's determining persistence based on index within the array. As a result, neither the pre-existing element nor the would-be element associated with the value 10
are returned by the enter()
function, since they're not considered to be a new elements, just a replacement of the data associated with the existing element.
If you want it to determine persistence based on the actual data, you have to pass a 2nd param to the data()
function:
data(dataset, function(d, i) { return d; })
// NOTE: "return i;" would have the same effect as not passing in the 2nd param
Now d3 will compare d
in each case –– which is 10 for the 0th element and null
for your pre-existing <p>
-- and decide, since they're not equal, that it needs to include the 0th element in the selection returned by enter()
, which will in turn append an element for it.
At this point, as far as d3 is concerned, the pre-existing element should be removed, and it will return it in the exit()
selection, so you can call remove()
on it.
Alternatively, depending on your goal, you can keep the data()
call single-param as you have it, and update its text –– not from the enter()
selection, but rather from the main, "updating" selection. See this jsFiddle.
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