Step 5: Draw data
The fifth step: drawing our data. This is an important lesson! We talk about data joins, which are one of the trickiest parts of d3, and necessary for updating our charts & binding our visualization to data.
This lesson preview is part of the Fullstack D3 Masterclass course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.
[00:00 - 00:03] All right, so here comes the fun part. We are ready to draw some data.
[00:04 - 00:11] So this is going to be a little bit different if we remember last lesson. We had this one line that covered all of our data points.
[00:12 - 00:25] So for this scatter plot, we're going to have one SVG element for each of the data points instead of one that covers all of them. So we have this cheat sheet of SVG elements.
[00:26 - 00:31] Most of them are going to be unfamiliar to you right now, which is OK. We'll cover them all by the end.
[00:32 - 00:40] So we can see our handy path element with that D attribute. For our scatter plot, we want to use a circle element.
[00:41 - 00:50] And we can see that it uses three attributes, CX, CY, and R. So R is going to be the radius, which is half of the width.
[00:51 - 01:00] So if we have a radius of 10, our circle will be 20 pixels wide. And then CX is the X position, CY is the Y position.
[01:01 - 01:10] Keep in mind that it's CX because it's coding for the center of the circle instead of the top left. So that's why it's CX instead of X.
[01:11 - 01:21] So let's go ahead and create a circle, just to make sure this is doing what we want it to do. So we're going to use that CX attribute.
[01:22 - 01:26] Let's set it to 100. Let's do the same for CY.
[01:27 - 01:37] And then we also need an R, I think by default, it's 0, so let's set that to 10. And all of a sudden, we have a dot.
[01:38 - 01:40] And this is great. We can set the R to 100.
[01:41 - 01:46] And now our circle is 200 pixels wide. Perfect.
[01:47 - 01:55] So how do we hook this up to our data? A naive approach might have you looping over the data.
[01:56 - 02:00] So let's test that out. So data.forEach.
[02:01 - 02:12] So each of these data points we're going to encode as a variable as d. And for each of these, we're going to create a new circle.
[02:13 - 02:27] So the CX is going to be-- we're going to use our X scale to move the X value. So our X successor is getting the X value from this data point.
[02:28 - 02:40] And then we're scaling it so that it fits on our X axis, moving from D-point to pixels to the right. And the same for the CY.
[02:41 - 02:50] We're using the Y scale and the Y accessor to scoot those dots down. And then let's switch this R to something like 5.
[02:51 - 02:55] So this is great. This is looking exactly how we want it to.
[02:56 - 03:16] The dots are encoded by D-point on the horizontal axis and the humidity on the vertical axis. Now while this works, this isn't usually what we do with D3 for two obvious reasons right now.
[03:17 - 03:30] The first is that nested code is great, except when it gets too nested. So a rule of thumb is that every time you nest your code, it gets a little bit harder to follow.
[03:31 - 03:43] And the bigger reason is that every time we run this code, we're creating new circles for every one of those data points. There's no link between this data point and the item in our data set.
[03:44 - 03:59] So we have no way of updating it other than just erasing it and redrawing everything. So instead, let's use something called the data join.
[04:00 - 04:11] So let's grab our bounds. And we've usually used select until now, but there's also this method called selectAll.
[04:12 - 04:22] So select makes sense when we want one element to be returned. So we're selecting that one element with an ID of wrapper.
[04:23 - 04:31] But now we want all elements that are circle. And this doesn't really make much sense right now.
[04:32 - 04:34] We don't have any circles. We're just getting started.
[04:35 - 04:42] But let's save this selection object to a variable called dot. And let's look at it.
[04:43 - 04:52] So here we see this selection object. We have a groups key and a parent's key.
[04:53 - 05:01] This parent has our bounds because we're using bounds.selectAll. And then groups is this node list with nothing in it.
[05:02 - 05:21] So the way we join this d3 selection object with our data set is we use dot data, the dot data method, and then we pass it our data set. So now if we look at this selection object, nothing really has changed about groups or parents.
[05:22 - 05:27] But we have this new enter key and a new exit key. So our exit key is empty.
[05:28 - 05:41] But our enter key has a list of 365 nodes. So each of these nodes is in the SVG name space, which makes sense because we're already within an SVG element.
[05:42 - 05:53] Its parent is the bounds. But it also has this data key that if we open it, we'll see this is one of our data points.
[05:54 - 06:08] So you'll see for each of these enter nodes, we have one of our data points linked to it. And there's 365 enter nodes, which means we have 365 elements that we want to put on the page.
[06:09 - 06:27] So if we look at this little schematic, we're seeing that we have these three different groups. One is a group of new elements that need to go on the page that are in our data set, but not on the DOM.
[06:28 - 06:43] The intersection of data set and DOM elements are existing elements that are both in the data set and already on the DOM that's in groups. And we have these old elements that are on the DOM, but not in this new data set.
[06:44 - 06:58] So we kind of want to get rid of them. So let's go ahead and we can use the enter method to grab either the new elements or the old elements.
[06:59 - 07:19] This existing middle of the Venn diagram, the groups key, those are the elements that are acted on for the current D3 selection. So if we do anything to this existing D3 selection, nothing will happen because our list is empty.
[07:20 - 07:28] There are all these undefined elements. And there's the interest 65 just to make space for the ones that we want to add.
[07:29 - 07:37] So we want to act on all of our new elements. So let's grab those using enter.
[07:38 - 07:48] So if we now look at the D3 selection, we can see that all of our enter nodes are within this groups key. And then for each of these, we want to append a circle.
[07:49 - 08:05] And in order to see these circles, let's just give them a radius of 10. So we can see we're adding elements to the page.
[08:06 - 08:17] If we look at this groups key now, we can see all of our new circles. There should be there in D3 65, lovely circles already on the page.
[08:18 - 08:26] Let's go ahead and mirror what we were doing before. So let's give it the CX attribute.
[08:27 - 08:52] Now, once we've done this, we saw that we had data for each of these elements. And I think if we inspect it, one of these circles in the inspector-- let's do this in the native browser, DevTools.
[08:53 - 09:18] Each one of these elements is going to have the data point coded onto the element in the DOM. So if we look at one of these circles, and we look at the properties-- it's not properties, which is it-- it's computed.
[09:19 - 09:31] Yeah, so if we look at the computed values-- no, this doesn't go in. Oh, look at that.
[09:32 - 09:37] That's deprecated. So let's look at them this way.
[09:38 - 09:55] So if we look at this circle, it has all these properties on it, which the DOM will use to affect presentation and interaction. But if we go all the way to the bottom, we have this data key that has all of this information from the data point.
[09:56 - 10:15] So this is how D3 is keeping track of which elements are already rendered and which aren't. So whenever we use this attribute method or a style method, if we pass a function as the second parameter, the function will receive that data point.
[10:16 - 10:29] So this D is going to be one of the elements within this data array. So we want to pass the scaled and accessed x value.
[10:30 - 10:41] And then we want to do the same for this y value. And let's bump that radius down a little bit.
[10:42 - 10:45] All right, awesome. So we're exactly where we were before.
[10:46 - 11:02] But let's see how this works when we run this method more than once. So let's encapsulate this within a draw.function that takes a new data set.
[11:03 - 11:18] And let's give it a color. So whenever draw.is run, it'll create new circles for anything within that data set of that specific color.
[11:19 - 11:28] So let's use that color to change the fill of each of these circles. And fill is kind of like background for an HTML element.
[11:29 - 11:35] But with SVG, we use fill. So let's draw dots with our data.
[11:36 - 11:41] Let's make them gray. So now we have one gray dot for every item in our data set.
[11:42 - 11:49] Now let's only do this for the first 100 elements within our data set. OK, great.
[11:50 - 12:11] And then after one second, let's do the same thing, but with the rest of our data set. So our entire data set, and let's make them this nice cornflower blue.
[12:12 - 12:20] So this is interesting. We are drawing our first 100 dots in gray.
[12:21 - 12:28] And then we're drawing the rest of them in blue. But we're not updating those gray dots, which are in this data set.
[12:29 - 12:34] We're not redrawing them as blue. And that's because we're only grabbing these new dots.
[12:35 - 12:47] So we can also save-- let's just look at dots again. And then use that dot selection object.
[12:48 - 13:04] And just reuse it this way when we're grabbing the entering elements. So if we look at this for the second time, and we already have 100 dots drawn, we can see here 100 of these elements will have a circle.
[13:05 - 13:25] And then the rest of them are undefined. And this is because the second time we run this function, we have 200 elements in this enter array and 100 will actually know elements in the exit array, because we don't want anything to go away that we've already drawn.
[13:26 - 13:39] But we're switching to changing the x, y, r, and fill attributes for everything within the enter array. But we want to do that for all of them.
[13:40 - 13:56] So let's go ahead and merge this new enter selection with the original selection so that we're updating all of the dots. So now we can see all of those dots that were gray before are now blue.
[13:57 - 14:08] So let's go ahead and put this back. We don't need to draw these dots more than once.
[14:09 - 14:18] We only want to draw them one time. And then let's keep them that cornflower blue, because it's a nice color.
[14:19 - 14:30] And one thing to note is this is probably very confusing. I know when I learned this for the first time, I was very confused.
[14:31 - 14:50] And because so many people are confused, they added this dot join method in some of the more recent versions of D3. So instead of enter append exit of the merge, you can now just do data dot join.
[14:51 - 15:01] You don't even need to merge them all together. So replacing all that code with just a dot join makes things a lot easier to understand.
[15:02 - 15:13] Kind of cleans up your code. I do like to talk about enter exit update, because these are important things to understand.
[15:14 - 15:27] This is what D3 is doing under the hood. But if all you want to do is draw these dots and update them whenever your data updates, then just use that join so much easier to handle.