AlexRothenberg

Learning D3.js by Building a Chart

If you’re doing any JavaScript you’ve probably heard of d3.js. In fact Github Graphs
are built with it so even if you weren’t aware, I’m sure you’ve seen it there.
But maybe you haven’t been sure how to get started using it yourself. Its not too hard once you have a mental model of how it works.

Today we’re going to walk through how to build a bar chart that shows some interesting weather data about Boston as a way to learn more about d3.js.

Before we get into any details here are a few big picture ideas to keep in mind

d3 is all about binding data with DOM elements.

d3 has an enormous API covering charts, maps, arrays, and all sorts of other things

svg is a great fit when you’re creating charts or other visualizations

Binding data to the DOM

The first thing to know is that d3 stands for Data-Driven Documents and its core job is to let you synchronize data to DOM elements.
Let’s start by seeing how we can use it to add some DOM elements.

1

<script src="http://d3js.org/d3.v3.min.js"></script>

Once we include the d3.js script in our page, we have a top level d3 object we can use to start adding elements.
If you haven’t used SVG (scalable vector graphics) its a series of tags added in HTML5 that let us define shapes on our page.
They can be styled with css and manipulated with javascript just like traditional html tags but are designed more for shapes than text so we can do all sorts of interesting drawings with them.

This looks kinda like jQuery but with a slightly different syntax and that’s not by accident. Let’s go through it line-by-line

Line 1 - We append a new svg element inside the body and return a d3 object representing that element.

Line 2-3 - D3 chains everything just like jQuery so we can set height and width attributes on the svg element

Line 4 - Add a g (or “group”) element within the svg

The page won’t look any different to a user because the svg and g elements don’t have any visual representation, however, if we were to inspect the DOM we’d see our tags in there

1
2
3
4
5

<body><svgheight="200"width="500"><g></g></svg></body>

We’re started on the document part so time to add the data. Here’s a simple tsv (tab-separated values) file of average snow totals in Boston by month (all the data files are on github if you want to look).

MonthAverageOct0.1Nov0.6Dec12Jan14.9Feb11.8Mar7Apr0.3

Now we will use the d3 tsv API for parsing our data and inserting an element for each row into the DOM.
Before we look at the code there are two concepts to mention briefly.
We’re going to create a bunch of rect elements within our svg.
As the name suggests the browser will draw them as rectangles.
Also the main mechanism behind d3 is joining data to the DOM. Basically a dataset is joined to a set of DOM elements and d3 ensures that there will be the same number of DOM elements as rows of data. Mike Bostock has explained this really well in thinking with joins.

So what does this look like in code?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

// This is unchangedvarsvg=d3.select('body').append('svg').attr('height','200').attr('width','500');varg=svg.append('g');varparseRow=function(row){return{month:row.Month,average:parseFloat(row.Average)}};d3.tsv('data/snow.tsv',parseRow,function(data){varrect=g.selectAll('.bar').data(data).enter().append('rect')rect.attr('class','bar').attr('width',10).attr('height',100).attr('x',function(d){returnd.month.charCodeAt(0)})})

Line 14 - Load and parse the snow.tsv file. We give it 2 callbacks, the first for formatting each row and the last to process the data by adding it to our DOM

Lines 7-12 - Format each row by converting the average column from a string to a number

Line 15 - Bind our data to all the elements with the bar class (it’s an empty set when the page loads but this becomes useful further on)

Line 16 - for each data item without a corresponding DOM element (that’s what enter() means) create a rect element

Lines 17-20 - set a bunch of positioning attributes on each rect. The attributes can either be a contant or a function that gets called with the data item d3 attached to this particular rect element.

Now we have a bar but they’re all on top of each other (mouse over what’s below to see)

Next we will look at some d3 chart APIs that can help us position the bars instead of spacing based on the first letter of each month.

Scaling with the D3 Scale API

What we need to do is scale both the x and y dimensions based on the data we have. The width of each bar depends on how many months we are displaying and also the height depends on the height of the month with the most snow.
Luckily d3 has us covered with its scales API.
We’re going to use an ordinal scale for the months and linear scale for the inches of snow.

We change the tsv callback to use these scales when setting the width, height, x & y attributes of each rect.

Lines 7-9 - Remember when we talked about thinking in joins and how d3 binds data to DOM elements? Previously we only had the enter() line which handles the case when there are more rows of data than DOM elements. Now we need to handle the opposite case when there are more DOM elements than rows of data. In that case we use exit() and delete those elements.

Animating with the d3 transtions API

It’s always fun to have things move around so in our last step we’re going to look at how to rearrange the chart to be in descending order or calendar order.
Once again there’s a d3 transition API to help.