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.
Here comes the fun part! Drawing our scatter plot dots will be different from how we drew our timeline. Remember that we had one line that covered all of the data points? For our scatter plot, we want one element per data point.
We'll want to use the
<circle> SVG element, which thankfully doesn't need a d attribute string. Instead, we'll give it cx and cy attributes, which set its x and y coordinates, respectively. These position the center of the circle, and the r attribute sets the circle's radius (half of its width or height).
Let's draw a circle in the center of our chart to test it out.
Starting to get SVG elements mixed up? No worries! We have an SVG elements cheat sheet PDF to help remember what elements exist and what attributes they want. Don't worry if you don't recognize any of the elements — we'll cover them all by the end of the course.
Great! Now let's add one of those for each day.
A straightforward way of drawing the dots would be to map over each element in the dataset and append a circle to our bounds.
Look at that! Now we're starting to get a better sense of our data.
While this method of drawing the dots works for now, there are a few issues we should address.
We're adding a level of nesting, which makes our code harder to follow.
If we run this function twice, we'll end up drawing two sets of dots. When we start updating our charts, we will want to draw and update our data with the same code to prevent repeating ourselves.
To address these issues and keep our code clean, let's handle the dots without using a loop.
Scratch that last block of code. D3 has functions that will help us address the above problems.
We'll start off by grabbing all
<circle> elements in a d3 selection object. Instead of using d3.selection's
.select() method, which returns one matching element, we'll use its
.selectAll() method, which returns an array of matching elements.
This will seem strange at first — we don't have any dots yet, why would we select something that doesn't exist? Don't worry! You'll soon become comfortable with this pattern.
We're creating a d3 selection that is aware of what elements already exist. If we had already drawn part of our dataset, this selection will be aware of what dots were already drawn, and which need to be added.
To tell the selection what our data look like, we'll pass our dataset to the selection's
When we call
.data() on our selection, we're joining our selected elements with our array of data points. The returned selection will have a list of existing elements, new elements that need to be added, and old elements that need to be removed.
We'll see these changes to our selection object in three ways:
our selection object is updated to contain any overlap between existing DOM elements and data points
an _enter key is added that lists any data points that don't already have an element rendered
an _exit key is added that lists any data points that are already rendered but aren't in the provided dataset
Let's get an idea of what that updated selection object looks like by logging it to the console.
Remember, the currently selected DOM elements are located under the
_groups key. Before we join our dataset to our selection, the selection just contains an empty array. That makes sense! There are no circles in bounds yet.