Binding Data
Of course, changing things at random is not really useful. More commonly, we would want to use
existing data to determine the appearance of our circles. Let's say we want these
circles
represent the numbers 500, 150, and 1000. In D3, the selection.data()
method binds
these numbers to the selected circles in this way:
15 16 17 18 |
<script> let circles = d3.selectAll("circle"); circles.data([500,150,1000]); circles.style("fill", "steelblue"); |
Data is specified here as an array of values; this mirrors the concept of a selection, which is an
array of selected elements. In the code above, the first number (the first datum, 500) is bound
to the first circle (the first element, based on the order in which they are defined in the
DOM), the second number is bound to the second circle, and so on.
After data is bound, it is accessible as the first argument to the anonymous data functions. We
could use any variable name for this argument, but the convention is to use the name d
to refer to the bound data. Thus, too set the area of the circles according to the data proportions,
we can use the square root of the data for each element to set the radius, as demonstrated in line
19:
15 16 17 18 19 20 |
<script> let circles = d3.selectAll("circle"); circles.data([500,150,1000]); circles.style("fill", "steelblue"); circles.attr("r", function(d,i) {return Math.sqrt(d)} ); circles.attr("cx", function(d,i) {return (i * 100) + 40} ); |
In line 20, you see the use of a second optional argument to each anonymous function you
can also use: the index of the element within its selection. The index is often
useful for positioning elements or setting other attributes sequentially. Like all
array
elements in Javascript, the index is 0-based
(i.e., the first
element is element 0). By convention, we usually refer to the index as parameter i
.
Edit your code now accordingly, to implement drawing the radius and position of the circles based on the data.
Creating Elements automatically from the Data
What if we had four numbers to display, rather than three? We wouldn't have enough circles,
and
we would need to create more elements to represent our data. You can append new nodes
manually,
but a more powerful alternative is the enter
selection computed by a data join.
When joining data to elements, D3 puts any leftover data -- or equivalently 'missing'
elements -- in the enter
selection. With only three circles, a fourth number
would
be put in the enter
selection, while the other three numbers are returned
directly by selection.data
. By appending to the enter
selection,
we
can create new circles for any missing data.
Taking this to the logical extreme, then, what if we have no existing
elements? Then we're joining data to an empty selection, and all data ends
up in the enter
selection.
This pattern is very common in D3 applications. You willl often see the
selectAll + data + enter + append
methods called sequentially, one
immediately after the other, chained together using Javascript's
so-called method chaining (the chaining together of methods by
using the .
notation).
Change your webpage to match Listing 7. As you can see,
we now start with an empty svg
and create the circles fully automatically
from the data. You now should get four circles!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <!-- First load the D3 javascript library: --> <script src="lib/d3.js"></script> </head> <body> <svg id="svg" height="500" width="500"> </svg> <script> let svg = d3.select("svg"); svg.selectAll("circle") .data([500, 150, 1000, 750]) .enter() .append("circle") .style("fill", "steelblue") .attr("cy", 60) .attr("r", function(d,i) {return Math.sqrt(d)} ) .attr("cx", function(d,i) {return (i * 100) + 30} ) ; </script> </body> </html> |
Note the first line of the script is now let svg =
d3.select("svg");
and the d3.selectAll
was replaced
by svg.selectAll
. This is done to make sure the newly created circles do not
just get appended to
the end of the file, but are properly 'attached' to the
(until then) empty svg
element.