Contents

How to Make your First Project in D3.js?

You have just heard about the power of D3.js but do not know where to start with?

Relax, you are in the good place!

Let me show you how to display a simple bar chart on your web site with D3.js.

At the end of this post, you should be able to plot this type of chart:

We will work with only three files:

  • index.html, a file that contains the structure of your website;

  • main.js, the heart of your project it allows you to create your chart, title, and axes;

  • revenue.csv, a very simple dataset with two columns (we will talk about it later).

There are available on my github account.

Be aware that the files contains already the solution. So if you want to know how to do step by step, read the article before.

Add D3.js in index.html file

Before using D3.js you need to import it in your project so that the browser can use it. To add D3.js in your project just copy-paste the tag below in the body’s tag of your .HTML file.

<script src="https://d3js.org/d3.v6.min.js"></script>

Make sure you have the latest version of D3 (v6 here).

In first time, your index.html file should look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!doctype html>
<html>
<head>
   <meta charset="utf-8">
   <title>My First D3.js Chart</title>
</head>

<body>

   <!-- Import D3 -->
   <script src="https://d3js.org/d3.v6.min.js"></script>

</body>
</html>

Connect your main.js File

Now, you should have an empty web page, let’s fill it out with your main.js file. To do this, you need to find a well place to your chart. Put this snippet just above the script’s tag.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="chart-area"></div>
Then, add the reference to the main.js file just below the importation of D3.js.
<script src="main.js"></script>

So, finally your index.html should look like this:
<!doctype html>
<html>
<head>
   <title>My First D3.js Chart</title>
</head>

<body>
   <!-- Insert chart area-->
   <div id="chart-area"></div>

   <!-- Import D3 -->
   <script src="https://d3js.org/d3.v6.min.js"></script>

   <!-- Custom JS -->
   <script src="main.js"></script>
</body>
</html>

Now, everything you will do in your main.js will affect your web page.

Scalable Vector Graphics

D3.js works with SVG (scalable vector graphics) to display charts. So first things you will do is to append an svg variable that contains the width and the height of your chart.

D3.select()

D3.js has a lot of methods, here you will use just few of them. The first is “.select”, it allows you to select an HTML item by id. Remember that above you defined a div tag which contains the “chart-area” id.

After selecting the good id (here the only), append to this div an svg which set the width and the height of your chart. Specifically, here the syntax to use:

1
2
3
4
const svg = d3.select("#chart-area")
    .append("svg")
    .attr("width", 600)
    .attr("height", 400)

As you see, in this example we use a width of 600 pixels and a height of 400 pixels.

You should understand that here you have created the foundations of your chart and in the future you will need to display multiple charts on a single web page. To do this in D3.js we use groups. Each item of your chart (rectangles, axes, title) should be in the same group. Let’s define our group like this:

1
2
const g = svg.append("g")
    .attr("transform", `translate(100, 30)`)

Note that a group is append to an svg object. Do not care about the attribute “transform”, we will discuss about it in a next post.

Draw Rectangles

For the following, you should know how to draw rectangles (remember that we are creating a bar plot) so let’s do it! As I said above, there are a lot of methods in D3.js and obviously there is one to create rectangles.

1
2
3
4
5
6
const rects = g.append("rect")
        .attr("y", 0)
        .attr("x", 0)
        .attr("width", 150)
        .attr("height", 300)
        .attr("fill", "#6a976a")

Here we add a rectangle “rect” to the group “g” with several attributes:

  • x
  • y
  • width
  • height
  • fill

“x” and “y” represent the position of the left-top corner of our rectangle. “width” and “height” is specified in pixels and “fill” is the color of our rectangle, here green in hex, you can also use simple strings to define color (“green”, “blue”, …). You should see now a rectangle on your web page.

Datasets

Let’s discuss about the revenue.csv file. As I said before, it contains two columns : “year” and “revenue” and seven rows.

You need to draw a rectangle for each rows to plot your bar chart. To do this we import the data with the .csv method then we use the .then to deal with our data.

1
2
3
d3.csv("revenues.csv").then(data => {
    // Code here
})

As you can see, we put the path of the revenues.csv file in the .csv() method and in the .then() method we can call the data with a variable (named “data” here). So now let’s draw our rectangles! Just copy-paste the code to draw a rectangle in the bracket.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
d3.csv("revenues.csv").then(data => {
    
    // Draw rectangles
    const rects = g.append("rect")
        .attr("y", 0)
        .attr("x", 0)
        .attr("width", 150)
        .attr("height", 300)
        .attr("fill", "#6a976a")

})

You still have just one rectangle on your website now. Let’s connect the height of this rectangle to the first year of our dataset. You can acces to the revenue in a row using .revenue so console.log(data[0].revenue) …give us 134300.

Scales

You should note that 134300 pixels is pretty huge and difficult to display in one screen. In D3, Scales are powerful tool to make your charts fits with your screen. There is several types of scales (linear, band, logarithmic,…) but do not worry about it we will dedicate a post for it. For revenue which correspond to our y axis we will use a linear scale. At this point, just trust me and add this snippet just before the rects definition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
d3.csv("revenues.csv").then(data => {
    // Scaling the y axis
    const y = d3.scaleLinear()
       .domain([0, d3.max(data, d => d.revenue)])    // input
       .range([340, 0])  // output

    // Draw rectangles
    const rects = g.append("rect")
        .attr("y", 0)
        .attr("x", 0)
        .attr("width", 150)
        .attr("height", 340 - y(data[0].revenue))
        .attr("fill", "#6a976a")
})

As you can see, I changed the height in the rects defintion. Like this, your rectangle should display correctly and with the good scale.

Let’s add the scale for x axis:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
d3.csv("revenues.csv").then(data => {
    // Scaling y axis
    const y = d3.scaleLinear()
       .domain([0, d3.max(data, d => d.revenue)])    // input
       .range([340, 0])  // output

    // Scaling x axis
    const x = d3.scaleBand()
        .domain(data.map(d => d.year))    // input
        .range([0, 480])
        .paddingInner(0.5)
        .paddingOuter(0.2)// ouput
    const yAxisCall = d3.axisLeft(y)
    .ticks(3)
    .tickFormat(d => "$"+d)
    g.append("g")
      .attr("class", "y axis")
      .call(yAxisCall)


    const rects = g.selectAll("rect")
        .data(data)

    rects.enter().append("rect")
        .attr("y", d => y(d.revenue))
        .attr("x", d => x(d.year))
        .attr("width", x.bandwidth)
        .attr("height", d => 340 - y(d.revenue))
        .attr("fill", "#6a976a")
})

We will change the width just later.

Now we need to make to connect the each revenue to a rectangle.

To do this we will select all the rows in our dataset with .selectAll method and append for each a rectangle just like this:

1
2
3
4
5
6
7
8
9
const rects = g.selectAll("rect")
    .data(data)

rects.enter().append("rect")
    .attr("y", 0)
    .attr("x", 0)
    .attr("width", 150)
    .attr("height", d => 340 - y(d.revenue))
    .attr("fill", "#6a976a")

Note that the height is modified here because now we are acting on all the dataset so we need to use a variable that will browse it (here “d”).

The issue now is that all our rectangles are in the same location because y and x are still unchanged. Use the Scales that we defined to deal with it.

1
2
3
4
5
6
rects.enter().append("rect")
    .attr("y", d => y(d.revenue))
    .attr("x", d => x(d.year))
    .attr("width", 150)
    .attr("height", d => 340 - y(d.revenue))
    .attr("fill", "#6a976a")

Nice, you now have all the bar displayed on your screen!

I supposed you have noticed that our rectangles overlap. 😢

So, It is now the time to change the width using x.bandwith, just change the rects definition:

1
2
3
4
5
6
7
8
9
const rects = g.selectAll("rect")
    .data(data)

rects.enter().append("rect")
    .attr("y", d => y(d.revenue))
    .attr("x", d => x(d.year))
    .attr("width", x.bandwidth)
    .attr("height", d => 340 - y(d.revenue))
    .attr("fill", "#6a976a")

It should look like this now:

Pretty nice no? Let’s add the axis now using .axisBottom and .axisLeft methods:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
d3.csv("revenues.csv").then(data => {
    // Scaling the y axis
    const y = d3.scaleLinear()
       .domain([0, d3.max(data, d => d.revenue)])    // input
       .range([340, 0])  // output

    // Scaling the x axis
    const x = d3.scaleBand()
        .domain(data.map(d => d.year))    // input
        .range([0, 480])
        .paddingInner(0.5)
        .paddingOuter(0.2)// ouput

    const xAxisCall = d3.axisBottom(x)
    g.append("g")
        .attr("class", "x axis")
        .attr("transform", `translate(0, 340)`)
        .call(xAxisCall)
        .selectAll("text")
        .attr("y", "10")
        .attr("x", "-5")
        .attr("text-anchor", "end")
        .attr("transform", "rotate(-40)")

    const yAxisCall = d3.axisLeft(y)
    .ticks(3)
    .tickFormat(d => "$"+d)
    g.append("g")
      .attr("class", "y axis")
      .call(yAxisCall)


    const rects = g.selectAll("rect")
        .data(data)

    rects.enter().append("rect")
        .attr("y", d => y(d.revenue))
        .attr("x", d => x(d.year))
        .attr("width", x.bandwidth)
        .attr("height", d => 340 - y(d.revenue))
        .attr("fill", "#6a976a")
})

We are almost there! We just need to add x and y labels and a title. Let’s do it by adding text objects to our Group “g” with some attributes. We do it just above the .csv() methods.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const svg = d3.select("#chart-area")
    .append("svg")
    .attr("width", 600)
    .attr("height", 400)

const g = svg.append("g")
    .attr("transform", `translate(100, 30)`)

// X label
g.append("text")
  .attr("class", "x axis-label")
  .attr("x", WIDTH / 2)
  .attr("y", HEIGHT + 50)
  .attr("font-size", "20px")
  .attr("text-anchor", "middle")
  .text("Year")

// Y label
g.append("text")
  .attr("class", "y axis-label")
  .attr("x", - (HEIGHT / 2))
  .attr("y", -60)
  .attr("font-size", "20px")
  .attr("text-anchor", "middle")
  .attr("transform", "rotate(-90)")
  .text("Revenue")

// Title
g.append("text")
  .attr("class", "y axis-label")
  .attr("x", (WIDTH/2.5 ))
  .attr("y", 0)
  .attr("font-size", "20px")
  .attr("text-anchor", "middle")
  .text("Revenue per year of your business")

Congratulations! You created your first bar chat with D3.js!

It’s a good start however this is just a tiny part of what D3.js can do. In next post, we will approach how to make a chart dynamic and interactive. Hope that you appreciated this one!

Note that here we did not talk about the file management and the organisation of our files because it is a small project but in case of bigger project you will need to know how to do it.

Note also that in next posts we will use variable for the width and the height of our svg and we will use margin too. I did not want to lose you with it but if you want you can experiment them.

Thanks for reading, See you!