A beginners guide to unpacking and customizing the elements of a D3 axis

Photo by Isaac Smith on Unsplash

Introduction

We will make a horizontal axis and customize it in different ways using D3 methods and CSS selections. First I’ll show you how to render a basic horizontal axis from a linear scale. Then we will learn how to customize the axis by changing how D3 uses the scale and the data to render the elements. We will explore the SVG elements that comprise a D3 axis, learn how to select these elements, and change some of the attributes. Specifically we will experiment with the following customizations:

Translating an axis

Changing number of ticks on the axis

Tick format

Custom tick labels

Make a grid with .tickSize()

Attributes of the .domain <path> element

Tick labels: fonts, size, and rotation

Selecting tick lines with CSS selections and changing their attributes.

If you are already familiar with D3 then you may want to skip the first part of this post. Let’s start with the basic setup for our html and Javascript files. We will not need a CSS file. The complete JS code for this post can be found here.

Basic setup

Make an HTML file with the following code. Download the file d3.min.js into a subfolder with the name “js”.

<!doctype html>

<html>

<head>

</head>

<body>

<script src="js/d3.min.js"></script>

<script src="js/script.js"></script>

</body>

</html>

We will use the usual margin convention suggested by Mike Bostock. This a typical template for D3 projects. Save this file in your “js” folder as script.js.

const margin = {top: 20, right: 10, bottom: 20, left: 10}; const width = 500 - margin.left - margin.right,

height = 300 - margin.top - margin.bottom; const g = d3.select("body").append("svg")

.attr("width", width + margin.left + margin.right)

.attr("height", height + margin.top + margin.bottom)

.append("g")

.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

Making a horizontal axis

Now that we have an SVG group where our chart will live we can make a simple horizontal axis. Here are the basic steps.

Make a linear scale. Make an axis generator from the scale. Call the generator on an SVG group in order to render the axis onto our html page.

Make a linear scale

Our axis requires a scale. A scale is a function that takes a data value as an input and returns a value in pixels. Let’s assume our data goes from 0 to 10000.

const min_data = 0, max_data = 10000; let xScale = d3.scaleLinear()

.domain([min_data, max_data])

.range([0, width]);

Make an axis generator

Axis generators input scales and can be used with .call to append svg elements that will add an axis to your data visualization. Since we want to create a horizontal axis we will choose d3.axisBottom(scale). Other options include d3.axisTop(scale), d3.axisRight(scale), and d3.axisLeft(scale). For a complete list, see the D3.js API reference.

let xAxisGenerator = d3.axisBottom(xScale);

Render the axis with .call

To see the axis in the browser we have to call it on an svg group.

let xAxis = g.append("g")

.call(xAxisGenerator);

Once you call the axis generator you should see the following at the top of the browser.

The variables xAxisGenerator and xAxis give us two different ways to customize an axis.

You can apply various D3 methods to xAxisGenerator to change the way D3 uses the scale to generate the axis. This happens before the axis is rendered. We can apply d3.select() and d3.selectAll() to to the variable xAxis to select and change the attributes of the svg elements that make up the axis after it is rendered.

First we will look at ways to use D3.js methods on xAxisGenerator to customize our axis.

Basic customization using the axis generator

Translating the axis to the bottom of the visualization

Usually we want a horizontal axis to be at the bottom. When we defined the axis generator we called d3.axisBottom. So why is the axis on the top? Well it turns out d3.axisBottom creates an axis where the tick marks are pointing downward (on the bottom of the axis). But it doesn’t place the axis itself on the bottom of the graph. The default position for axes is at the upper left. To get the axis to the bottom of the graph we have to translate it ourselves.

xAxis.attr("transform",`translate(${0},${height - 20})`);

Changing the number of ticks

Suppose we want to have only 3 ticks instead of the 11 we currently have. You can accomplish this with the .ticks() method called on the axis generator. Insert this code before you called the axis.

xAxisGenerator.ticks(3);

Now we should see the following axis positioned at the bottom.

The ticks values were automatically chosen when we defined the axis generator. But we can actually give custom tick values.

xAxisGenerator.ticks(3);

xAxisGenerator.tickValues([3000,5000,9000]);

Now we have the following.

Tick format

We can change the tick format with the .tickFormat() method by passing in one of D3’s built-in formats. For example, if we want to change 3,000 to its SI unit, 3K we use d3.format(“.2s”).

xAxisGenerator.tickFormat(d3.format(".2s"));

For a complete list of D3 formats click here.

Custom tick labels

We can actually use .tickFormat() to completely customize our tick labels. To make custom tick labels we do the following.

Make an array of the labels you want in the order you want them based on the number of ticks. Pass a function that returns the label for the index of each tick. The function has the data values of the ticks and the index of the list of ticks as inputs.

Suppose we want our tick labels to be A, B, and C instead of numbers.

let tickLabels = ['A','B','C'];

xAxisGenerator.tickFormat((d,i) => tickLabels[i]);

Make a grid with .tickSize()

The usual way to make a grid for a graph is to manipulate tick size. We can do this by passing a negative number to the .tickSize() method.

xAxisGenerator.tickSize(-200);

Inspecting the SVG elements that form an axis

To really customize our axes we need to change the attributes of the svg elements that were created when we called the axis generator. Let’s inspect the page with Chrome’s developer tools. You should see something very similar to this.

Let’s go through the axis DOM piece-by-piece. The outer <svg> and <g> groups were created in the very beginning of this post to contain our data visualization. The third tag in our hierarchy, <g>, contains our axis which is made of four types of svg elements each type has a specific class. Here is a sketch of the html from the top of the axis to the end of the first tick.

<path class="domain"></path>

<g class="tick">

<line></line>

<text></text>

</g>

We can change how our axis appears by changing the attributes of these SVG elements by using d3.select() and d3.selectAll() applied to the variable xAxis.

Customizing the SVG elements of an axis

Domain <path> element

The first SVG element in our axis is a path.

<path class="domain" stroke="#000" d="M0.5,-200V0.5H470.5V-200"></path>

This path outlines our axis. To change its attributes we can select it by its class, “.domain”. Let’s change its stroke from black to my favorite color, #E04836. Also change its stroke-width to 10 and opacity to .6.

xAxis.select(".domain")

.attr("stroke","#E04836")

.attr("stroke-width","6")

.attr("opacity",".6");

If you want the path to completely disappear you can use the .remove() method on the selection. You will be left with just the tick labels and the tick lines.

xAxis.select(".domain").remove();

There are many attributes that can be applied to paths. For example, we can use stroke-dasharray to make dashed lines.

xAxis.select(".domain")

.attr("stroke","#E04836")

.attr("stroke-width","10")

.attr("opacity",".6")

.attr("stroke-dasharray","4");

See the Mozilla documentation for a list of all possible attributes for paths.

Ticks contain <line> and <text> elements.

Let’s look at the html for the first tick.

<g class=”tick” opacity=”1" transform=”translate(141.5,0)”>

<line stroke=”#000" y2=”-200"></line>

<text fill=”#000" y=”3" dy=”0.71em”>A</text>

</g>

Let’s increase the font size for the first tick label. Once again we select this element, first by class then by tag, from the xAxis.

xAxis.select(".tick text")

.attr("font-size","20");

To increase the size of all three tick labels, we only need to replace .select() with D3’s .selectAll(). So let’s increase the size of all of the tick labels, rotate them 15 degrees clockwise, and change the font-family to cursive.

xAxis.selectAll(".tick text")

.attr("font-size","20")

.attr("rotate","15")

.attr("font-family","cursive");

For a list of font families and attributes for svg text see Mozilla documentation.

Lastly we can customize the tick lines themselves. Let’s say we want to change the color all the lines to steel blue.

xAxis.selectAll(".tick line")

.attr("stroke","steelBlue");

We can get as fancy as we want with our css selection techniques. Let’s select only the second line and increase its width by using the nth child selector. Notice that the second tick line is the line in the third element in our axis; the first element is a path.

xAxis.select(":nth-child(3) line")

.attr("stroke-width","10");

Conclusion

We learned how to customize a D3 axis in two different ways: by altering the axis generator and by using D3 to select the SVG elements that make up the axis. You can discover many more options by reading the D3 documentation for axis types and Mozilla’s documentation for SVG lines, paths, and text.

Big thanks to Mike Bostock for making such a cool library.