I’ve been on a mission to relearn the fundamentals of D3.js from the ground up. This tutorial is a way to apply what I learned about data joins, click events, and selections. Along the way I learned about building arrays.
I also wrote this up as a block for those interested.
We want to make a 10x10 grid using D3.js. D3’s strength is transforming DOM elements using data. This means we’ll need some data and we’ll want to use
Picture a grid in your head. It is made up of rows and columns of squares. Since this is ultimately going to be represented by an SVG, let’s think about how an SVG is structured:
What you see here is a basic structure of rows and columns. That means that when we make our data array, we want to make a nested array of rows and cells/columns inside those rows. We’ll need to use iteration to do this. Easy-peasy.
The other question we’ll have when making these arrays is, “What attributes will this grid need?”. Think about how you’d draw a grid: You start in the upper right corner of a piece of paper, draw a 1x1 square, move over the width of 1 square and draw another, and repeat until you get to the end of the row. Then you’d go back to the first square, draw one underneath it, and repeat the process. Here we’ve described positions, widths, and heights. In SVG world these are
Here is the function I’m using to create the underlying data for the upcoming grid. It makes an array that holds 10 other arrays, which each hold 10 values:
Making a Grid with D3 Data Joins
We made a cool array above and now we’ll make the data correspond to
svg:rect objects to make our grid. First we’ll need to make a div to append everything to (and, of course, don’t forget to include the latest version of D3 in your header):
Now we need to assign our data to a variable so we can access it:
Next, let’s append an SVG to the div we made and set its width and height attributes:
Next, we can apply what we learned in Mike Bostock’s Thinking With Joins to make our rows:
And finally we make the individual cells/columns. Translating the data is a bit trickier, but the key is understanding that we are doing a
selectAll on the rows, which means that any reference to data is to the contents of the single array that is bound to that row. We’ll then use a key function to access the attributes we defined (x, y, width, height):
You’ll note that I added style fill and stroke attributes to make the grid visible.
When we put it all together, here is what we get:
Note: If you are viewing this on your phone, you might want to switch over to a tablet or desktop. I haven’t optimized this example for mobile because that will needlessly complicate it.
Cool, huh? Go ahead and inspect the element and marvel at your find handiwork. Then change the fill, stroke, width, and height attributes and see how it changes.
Adding Click Functions
Let’s have some fun and add click events to the individual cells. I want to have cells turn blue on the first click, orange on the second, grey on the third, and white again on the fourth. Since D3 is data-driven, we’ll need to add some click data to the arrays and then add functions to change it and set colors based on the number of clicks.
Let’s break down that
- When you click on a cell, it increases the click variable (originally set at 0) by 1.
- The if statements set the color based on how many times it has been clicked mod 4. This satisfies the UI of only having four states: white, blue, orange, and grey. If you go to your console and call up the data for a certain cell, you’ll see that the full number of clicks is available.
Here it is. Click away!
Randomized click counts
What happens when we randomize click counts when we create the data array?
Math.random() returns a number between 0 and 1, inclusive. Multiple that by 100 if you want a number between 1 and 100.
It changes when you refresh!
Mouseovers: Even more fun with a bigger grid
What happens when we change the click event to a mouseover event and make a bigger grid? It becomes a lot more fun. I’ll leave the implementation as an exercise to the reader. If you’ve been following along and writing this yourself instead of copying and pasting, you probably already know which variables and events to change: