.enter
and .append
.enter
?When we first started learning about SVGs, we thought we knew everything!
We’d make a chart using rect
elements, designing each and every data point manually.
We could have rested there, but we were bigger! We were badder! We were programmers!
Instead of going into each and every rect
to manually set the width, we decided to use d3. We’d bind our data, and then use that data to set the width.
Let’s use d3 to automatically set the rect
width using data.
That way we could change the data from being [ 300, 50, 150, 355 ]
to being [ 20, 80, 1, 9 ]
to being [ 1, 0, 0, 400 ]
and the chart would just update automatically!
So cool! So fun!
…..until we get more data!
If you have four rect
elements but five data points, what happens?
Even after you bind, you still only have four rectangles.
So the four data points that have corresponding rect
s work out, but the fifth data point without a rect
just plain doesn’t! Now we have two options
rect
Method 1 sounds okay until you realize what happens if you need more data points. What if you have eight, or thirty, or two hundred?
Are you going to add each one of those <rect>
elements in there? You probably don’t want to do that manually.
For Method 2 I usually skip the crying part and just go straight to d3. Let’s do that.
.enter
and .append
After you bring data, .enter()
is a way of talking about all of the data points that don’t have an element yet.
We have 3 elements, but only 5 data points.
data point | rect |
---|---|
2 | Yes |
4 | Yes |
8 | Yes |
16 | No! |
32 | No! |
As a result, we’re left with two data points not represented. If we try to change attributes with rectangles
, it will only affect the first three!
In order to access the two other elements, we user .enter()
.
Even though we can access them now, there are still only three rect
elements on the page. With five data points, we need two more rect
elements.
In order to add the elements, you use .enter()
’s best friend: .append
. .append
will… append a new element for each of your data points.
In this case, we need to use new_rects.enter().append('rect')
. .enter()
accesses the lonely data points, and then append
will add a rectangle for each of them.
new_rects
loops through those two lonely data points, and adds a rect
for each of them.
Now you have five rectangles!
BUT.
Your first three rectangles are in var rectangles
, and your second (new) three rectangles are in var new_rects
. They need heights and widths and all of that, but because they’re in different variables, you’re going to have to set them both separately.
You’ll need to give rectangles
a width, and new_rects
a width. Separately.
You’ll need to give rectangles
a fill color, and new_rects
a fill color. Separately.
This is stupid! Can’t we just get them all on the same team?
The answer is yes,
Here’s the secret: delete all of the rect
s inside of the SVG. Now all of the rects are new.
If they’re all new, that means they’re all represented by .enter()
. new_rects
will have five, and rectangles
will have zero!
Usually it looks like this:
The other big thing you need to make a note of that the attr
function isn’t just function(d) { }
any more, but function(d, i) { }
. i
is an index, and it keeps track of how many elements you’ve gone through.
You can can use this with a little multiplication to space things out. Let’s look at what happens each time we go through attr
:
time through | data point d |
index i |
y |
---|---|---|---|
First | 10 | 0 | 0 * 20 = 0 |
Second | 70 | 1 | 1 * 20 = 20 |
Third | 30 | 2 | 2 * 20 = 40 |
Fourth | 40 | 3 | 4 * 20 = 60 |
Fifth | 90 | 4 | 4 * 20 = 80 |
Each time we go through to a new data point, i
gets one bigger. We multiply that out and voila! We have the y
offset that we need.
You know what’s the best part of this? The fact that now that we have enter
and append
we can add a million things to our chart. With five data points, we can automatically add not only five rectangles, but five of anything else! Like, oh, I don’t know, annotations?
For example!!!