D3.js — How to Make a Beautiful Bar Chart With The Most Powerful Visualization Library
By: Dario Radečić
Tags: Towards Data Science, Data Visualization, JavaScript, Data Science, D3js

Let’s make a chart

First things first — the data. We’ll store some dummy data in the JSON format. I’ve named mine sales.json and it looks like this:

[

{“Period”: “Q1–2020”, “Amount”: 1000000},

{“Period”: “Q2–2020”, “Amount”: 875000},

{“Period”: “Q3–2020”, “Amount”: 920000},

{“Period”: “Q4–2020”, “Amount”: 400000}

]

And that’s it for the data. Next, we need the HTML file. Don’t worry if you don’t know HTML, as it’s fairly straightforward, and we won’t spend much time on it.

For simplicity's sake, I’ve decided to embed CSS and JavaScript to the HTML file, but feel free to separate them if you wish. My file is named barchart.html :

<!DOCTYPE html>

<html lang=”en”>

<head>

<meta charset=”UTF-8">

<meta name=”viewport” content=”width=device-width, initial-scale=1.0">

<title>D3.JS Bar Chart</title>

</head>

<style>

rect.bar-rect { fill: #189ad3; }

rect.bar-rect:hover {

fill: #107dac;

transition: all .2s;

}

</style> <body>

<div class=”canvas”></div>

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

// Everything else will go here

</script> </body>

</html>

This is a starter template. Don’t worry about the CSS, I’ve used it just to color the bars in their normal state and in the hover state.

We can now start with the chart itself!

A bit of preparation

Let’s get started with the real deal. Our chart section needs some dimension info — like width and height. Also, we don’t want to make the chart occupy the entirety of the chart section, so we’ll need to add some margins on the top, right, bottom, and left.

To do so we’ll set up a couple of constants:

const width = 1000;

const height = 600;

const margin = {‘top’: 20, ‘right’: 20, ‘bottom’: 100, ‘left’: 100};

const graphWidth = width — margin.left — margin.right;

const graphHeight = height — margin.top — margin.bottom;

I think those are pretty much self-explanatory. If not, here’s a quick clarification:

The entire chart area will occupy 1000 x 600 pixels

The chart itself will be inside the chart area and will have margins on all sides

Margins on the bottom and on the left are larger because we will put the axes there

As simple as that. Let’s proceed.

Initial setup

Okay, we’re ready to proceed with the fun part. We need to somehow select the div with the class of canvas , as our chart will be stored there. In it, we’ll create a svg (remember that D3 outputs SVGs) and set its height and width to the ones declared earlier.

Next, we can insert the graph element into the svg element, and set it dimensions to ones declared earlier. We don’t want it to start at the top left corner of the svg , so we need to translate it accordingly.

Finally, we can declare constants for both X and Y-axis groups (don’t worry about those for a moment).

Here’s the code:

const svg = d3.select(‘.canvas’)

.append(‘svg’)

.attr(‘width’, width)

.attr(‘height’, height); const graph = svg.append(‘g’)

.attr(‘width’, graphWidth)

.attr(‘height’, graphHeight)

.attr(‘transform’, `translate(${margin.left}, ${margin.top})`); const gXAxis = graph.append(‘g’)

.attr(‘transform’, `translate(0, ${graphHeight})`); const gYAxis = graph.append('g')

If you were to open the HTML file now there’s nothing you would see, but that doesn’t mean nothing is happening. Just pop up the console and go to the elements inspector. Here’s what you should see:

Awesome! Let’s finish this thing next.

Drawing the chart

And now the moment you’ve been waiting for. We still have a lot of things to do, so let’s get right to it.

First of all, we need to read our JSON data somehow. Then, we’ll declare scales in constants x and y respectively, and those are there to ensure that individual bars don’t overflow the svg container by accident.

Next, we need to make a rect element (rectangle) for every entry we have in the dataset. Each rectangle has its height and width, X and Y values, and we’ve also added a custom class to it, just so it’s easier to style with CSS.

Pretty much the same needs to be done afterward, after the enter function, and after that we can set up the axes as we wish.

Our chart will have only 5 ticks on the Y-axis, and the values on the same axis will be formatted as currency. And yeah, axis label sizes are set to 14.

It’s a lot of code to write, so take your time to understand what each part does:

d3.json(‘sales.json’).then(data => {

const y = d3.scaleLinear()

.domain([0, d3.max(data, d => d.Amount)])

.range([graphHeight, 0]); const x = d3.scaleBand()

.domain(data.map(item => item.Period))

.range([0, 500])

.paddingInner(0.2)

.paddingOuter(0.2); const rects = graph.selectAll(‘rect’)

.data(data); rects.attr(‘width’, x.bandwidth)

.attr(‘class’, ‘bar-rect’)

.attr(‘height’, d => graphHeight — y(d.Amount))

.attr(‘x’, d => x(d.Period))

.attr(‘y’, d => y(d.Amount)); rects.enter()

.append(‘rect’)

.attr(‘class’, ‘bar-rect’)

.attr(‘width’, x.bandwidth)

.attr(‘height’, d => graphHeight — y(d.Amount))

.attr(‘x’, d => x(d.Period))

.attr(‘y’, d => y(d.Amount)); const xAxis = d3.axisBottom(x);

const yAxis = d3.axisLeft(y)

.ticks(5)

.tickFormat(d => `USD ${d / 1000}K`); gXAxis.call(xAxis);

gYAxis.call(yAxis); gXAxis.selectAll(‘text’)

.style(‘font-size’, 14);



gYAxis.selectAll(‘text’)

.style(‘font-size’, 14);

});

If you were to refresh the HTML page now, you would see a chart presented on it. Not only that, but we’ve also added different color on bar hover. Nice!

That’s it for today. Let’s wrap things up in the next section.