Tutorial: Building Interactive Bar Charts with JavaScript

Translator: OFED

Recently, we were fortunate enough to work on a machine learning project involving libraries like React and D3.js. Among many tasks, I developed several diagrams to show the processing results of machine learning models such as Naive Bayes, in the form of line charts or grouped bar charts.

In this article I will walk you through the process of using D3.js and demonstrate the basic use of the library with a simple bar chart example.

After reading this article, you will have learned how to easily create similar D3.js diagrams:

Here is the complete source code

We at RisingStack love the JavaScript ecosystem, front-end, back-end. Personally, I’m interested in both the front and back ends. With back-end development, I can see through the underlying business logic of my application, while also having the opportunity to create amazing effects on the front end. This is where D3.js comes in!

What is D3.js?

D3.js is a JavaScript library that can manipulate documents based on data.

“D3 helps you display data using HTML, CSS, SVG and Canvas. D3 follows existing Web standards, can run independently in modern browsers without the need for any other framework, and combines powerful visual components to drive DOM manipulation.” – d3js.org

Why create a chart with D3.js in the first place? Why not just show pictures?

Diagrams are based on information from third party sources and require dynamic visualization at rendering time. In addition, SVG is a very powerful tool that is well suited for this application scenario.

Let’s see what SVG is good for first.

The advantages of SVG

SVG stands for scalable vector graphics and is technically an XML-based markup language.

It is usually used to draw vector graphics, such as lines and shapes, or to modify existing images. You can find a list of available elements here.

Advantages:

  • Support for all major browsers;
  • DOM interface, no need for third-party libraries;
  • Scalable to maintain high resolution;
  • Smaller in size than other image formats.

Disadvantages:

  • Can only display two-dimensional images;
  • Long learning curve;
  • For computationally intensive operations, rendering can take a long time.

SVG, for all its shortcomings, is a great tool for displaying ICONS, logos, illustrations, or diagrams like those mentioned in this article.

Start using D3.js

I chose to start with the bar chart because it represents a visual element of low complexity, and it also teaches the basics of d3.js itself. I kid you not, D3 provides a great set of tools for visualizing data. Check out its Github page and enjoy some very good use cases!

A bar chart can be horizontal or vertical, depending on its orientation. Let’s start with a vertical bar chart.

In this chart, I’ll show the top 10 most popular programming languages based on Stack Overlow’s 2018 Developer survey.

Draw up!

SVG’s coordinate system starts at the top left corner (0; 0). Plus x to the right, plus y down. Therefore, the height of SVG must be taken into account when calculating the y coordinates of elements.

Background knowledge is about enough, let’s masturbate code!

I want to create a chart that is 1000 pixels wide and 600 pixels high.

<body>
    <svg />
</body>
<script>
    const margin = 60;
    const width = 1000 - 2 * margin;
    const height = 600 - 2 * margin;

    const svg = d3.select('svg');
</script>Copy the code

In the code snippet above, I selected the < SVG > element created by HTML with D3 select. This selection method accepts various types of selector strings and returns the first matching element. If you want to get all the matching elements, use selectAll.

I also define a margin value that gives the chart a bit of spacing. Spacing can also be applied to

elements, moving the desired value with Translate. From now on, I’ll draw in this group, making sure to keep a reasonable distance from the rest of the page.

const chart = svg.append('g')
    .attr('transform', `translate(${margin}, ${margin})`);Copy the code

Adding attributes to an element is as simple as calling the attR method. The first argument to the method receives an attribute for the selected DOM element. The second argument is the value of the property or the callback function that returns its value. The above code simply moves the origin of the diagram to SVG’s (60; 60) position.

Data source format supported by d3.js

To start drawing, I need to define the data source to use. In this tutorial, I use a simple JavaScript array that holds the language names and their percentage of objects, but it’s important to note that D3.js supports multiple data formats.

The library has built-in functionality for loading data from sources such as XMLHttpRequest,.csv files, and text files. Each of these data sources may contain the data available to D3.js, and the most important thing is to build them into arrays. Note that starting with version 5.0, the D3 library uses Promises instead of callbacks to load data, a backward-incompatible change.

Scale, coordinate axes

Let’s continue our discussion of the axes of the chart. To draw the Y-axis, I need to set the minimum and maximum to 0 and 100, respectively.

In this tutorial, I’m looking at using percentages, but there are other useful functions for data types besides numbers that I’ll explain later.

I have to divide the height of the chart equally between these two values. To do this, I created a scaling function.

const yScale = d3.scaleLinear()
    .range([height, 0])
    .domain([0, 100]);Copy the code

Linear scaling is the most common type of scaling. It converts a continuous input range to a continuous output range. Note the range and domain methods. The first range method should take the length between the domain boundaries.

Remember, the SVG coordinate system starts at the top left corner, which is why range takes height as the first argument instead of zero.

Creating an axis on the left is as simple as adding another group, calling D3’s axisLeft method and taking the scaling function as a parameter.

chart.append('g')
    .call(d3.axisLeft(yScale));Copy the code

Now, let’s go ahead and add the X-axis.

const xScale = d3.scaleBand() .range([0, Width]).domain(sample.map((s) => s.language)).padding(0.2) chart.append('g').attr('transform', 'translate' (0, 0) ${height})`) .call(d3.axisBottom(xScale));Copy the code

Note that I created the X-axis using the scaleBand method, which divides the X-axis into segments and uses the remaining gaps to calculate the coordinates and widths of the histogram.

D3.js can handle many other date types as well. ScaleTime is very similar to scaleLinear, except that the domain is an array of dates.

Use d3.js to draw the bar chart

Think about what kind of input we need to draw the bars. Each of these represents a value represented in simple shapes, especially rectangles. In the next piece of code, I add them to the group elements I’ve created.

chart.selectAll()
    .data(goals)
    .enter()
    .append('rect')
    .attr('x', (s) => xScale(s.language))
    .attr('y', (s) => yScale(s.value))
    .attr('height', (s) => height - yScale(s.value))
    .attr('width', xScale.bandwidth())Copy the code

First, I selectAll the elements on the diagram and return the result as empty. The data function then tells the DOM how many elements should be updated based on the array length. If there is more data than DOM, Enter identifies the missing element. Enter returns the element to be added. Typically, the append method is followed by adding elements to the DOM.

Basically, I append a rectangle to each item of the array with d3.js.

Currently only rectangles with no width and height are added on top of each other. These two properties must be computed using the previous scaling function.

I called the attr method to add the rectangular coordinates. The second argument can be a callback, which returns three arguments: the currently bound data, the index, and all data arrays.

.attr(' x ', (actual, index, array) => xScale(actual. Value))Copy the code

The scale function returns the coordinates of the values of the given range. Calculating coordinates is a piece of cake. The trick is to use the height of the column. The calculated y coordinate must be subtracted from the height of the chart to get the correct column values.

The scaling function is also used to define the width of the rectangle. ScaleBand has a bandwidth function that returns the calculated width of an element based on the set spacing.

Nice work, but not that fancy, right?

To prevent visual fatigue, let’s add some information to improve the visual effects!

Tips for making bar charts

There are some ground rules worth mentioning.

  • Avoid using 3D effects;
  • Visually sort data points – alphabetically or numerically;
  • Keep a certain distance between the columns;
  • The Y-axis starts at zero, not minimum;
  • Use a uniform color;
  • Add axis label, title, guide line.

D3.js grid system

I want to add a grid in the background to highlight those values.

Both vertical and horizontal lines can be added, my suggestion is to add only one. Too many lines can be distracting. The following code snippet demonstrates how to add horizontal and vertical grids.

chart.append('g')
    .attr('class', 'grid')
    .attr('transform', `translate(0, ${height})`)
    .call(d3.axisBottom()
        .scale(xScale)
        .tickSize(-height, 0, 0)
        .tickFormat(''))

chart.append('g')
    .attr('class', 'grid')
    .call(d3.axisLeft()
        .scale(yScale)
        .tickSize(-width, 0, 0)
        .tickFormat(''))Copy the code

In this case, I prefer vertical grids because they guide the eye and keep the overall picture brief.

Tags in d3.js

I also want to add some text guidance to make the chart more comprehensive. Let’s give the chart a name and label the axes.

Text is an SVG element that can also be added to AN SVG or grouping. They can be positioned using X and Y coordinates, and text alignment is achieved through the text-anchor property. To add tag text, you simply call the Text method on the text element.

SVG. Append ('text').attr('x', -(height / 2) -margin).attr('y', margin / 2.4).attr('transform', 'rotate(-90)') .attr('text-anchor', 'middle') .text('Love meter (%)') svg.append('text') .attr('x', width / 2 + margin) .attr('y', 40) .attr('text-anchor', 'middle') .text('Most loved programming languages in 2018')Copy the code

Interact with D3. Js

Our charts are already rich, but we can still add some interaction.

The following code demonstrates how to add event listeners to an SVG element.

On ('mouseenter', function (actual, I) {d3.select(this).attr(' opacity ', 0.5)}). On ('mouseleave ', Function (actual, I) {d3.select(this).attr(' opacity ', 1)})Copy the code

Notice that I used function expressions instead of arrow functions because I access elements through the this keyword.

When you hover over a selected SVG element, its transparency becomes half of its original value, and when you leave the element, its transparency returns to its original value.

You can also get the mouse coordinates using d3.mouse. It returns an array with x and y coordinates. You can do this by displaying a hint where the cursor is.

Creating jaw-dropping charts is not that simple.

The wisdom of graphic designers, UX researchers, and others may be needed. The following examples show several possibilities for improving the effect of a chart!

Our chart shows very similar values, so to highlight the differences between the bar values, I added a MouseEnter event. Whenever the user hovers over a particular column, a horizontal line is drawn at the top of the column. In addition, I calculated the difference with the other bars and showed it on the corresponding bar.

Neat, isn’t it? I also added transparency to this example and increased the width of the bar.

.on(' mouseenter ', function (s, I) {d3.select(this).transition().duration(300).attr('opacity', 0.6).attr('x', (a) => xScale(a.language) - 5) .attr('width', xScale.bandwidth() + 10) chart.append('line') .attr('x1', 0) .attr('y1', Y). Attr (' x2, width). Attr (' y2, y). Attr (the 'stroke', 'red') / / partial implementation, the overall effect see source})Copy the code

The transition method says I want to animate the DOM changes. Its time interval is set using the duration function, which takes milliseconds. The transitions above will dilute the ribbon color and increase the width of the strip.

To draw an SVG line, I need a starting and ending point. This can be set by the x1, y1 and x2, y2 coordinates. The line is not visible until I set its color with the stroke property.

Only the MouseEnter event section is shown here. Remember, you must restore or delete changes on the Mouseout event. The complete source code is provided at the end of this article.

Let’s add some styles to the diagram.

Review what we’ve done so far and how to dress up diagrams with styles. You can add class attributes to SVG elements using the attr method you used earlier.

Instead of being a rigid static image, our charts are rich in features that show the difference between the bars when you hover over them. Headings explain the background of the table, and labels help identify the units of measurement for the axes. I also added a new label in the lower right corner that identifies the source of the data.

All that’s left is color and font!

The chart on a dark background makes the bright bars look cool. I also used open Sans and set different sizes and thicknesses for different labels.

Notice that dotted line? It is implemented through the stroke-width and stroke-dasharray attributes. Using stroke-Dasharray, you can define the pattern and spacing of dashed lines to change the outline of the shape.

line#limit { stroke: #FED966; stroke-width: 3; stroke-dasharray: 3 6; } .grid path { stroke-width: 3; } .grid .tick line { stroke: #9FAAAE; Stroke - opacity: 0.2; }Copy the code

Grid lines are neat, I use stroke-width: 0 for the path elements in the group, and to hide the frame of the table, I also make the lines less visible by setting their transparency.

All other CSS references for font size and color can be found in the source code.

To wrap up our d3.js bar chart tutorial

D3.js is an amazing DOM manipulation library. Its interior contains countless treasures to explore (not buried, exactly, and well-documented). This article uses only the tip of its toolset to create a remarkable bar chart.

Keep exploring and the visual effects will be spectacular!

Here is a link to the sample source code for this article.

Have you ever done something cool with D3.js? Share with us! If you have any questions, or would like another tutorial on this topic, leave a comment!

Thanks for reading, see you next time!