In this tutorial, you’ll learn how to visualize data by taking advantage of the DataTables.js and Highcharts.js JavaScript libraries.
Here’s what we’re going to build (check out the larger version for a better experience):
Required Libraries
For the purposes of this example, we’ll have to load the following libraries in our pen:
- jQuery
- DataTables.js
- Highcharts.js
With that in mind, if you look under the Settings tab, you’ll see that I’ve included one external CSS file:
In the same way, I’ve also included four external JavaScript files:
Note: we had to add jQuery to our project because DataTables.js is a jQuery plugin. However, keep in mind that Highcharts.js is a pure JavaScript library, and thus doesn’t require jQuery.
The HTML
To kick things off we define an element with the class of container
which contains two sub-elements:
- A table with 26 rows. The first row refers to the table headers
th
, while the other 25 rows carry country details. The source of our data for this example is worldometers.info. - An empty
div
which will hold the chart.
Here’s the HTML structure:
Country Population (2017) Density (P/Km²) Med. Age China 1,409,517,397 150 37 It’s worth mentioning that, for the sake of simplicity, we’ve specified the aforementioned hard-coded table data. In a real project though, the table might be created dynamically.
With the markup ready, and a background color added for clarity, the project looks like this:
The CSS
At this point, we define some basic styles, like so:
.container { display: flex; flex-wrap: wrap; align-items: center; padding: 0 10px; } #dt-table_wrapper { width: 35%; margin-right: 2%; } #chart { width: 63%; } table { text-align: left; } @media screen and (max-width: 1200px) { #dt-table_wrapper, #chart { width: 100%; } #dt-table_wrapper { margin-right: 0; } }It’s important to understand that:
- The
#dt-table_wrapper
doesn’t exist in our markup. It’s added by the DataTables as soon as we initialize it.- While we define a few basic rules for small screens, note that the demo won’t be fully responsive. There are a lot of things we can do to make the table and chart look better on small screens. For example, for DataTables there’s a Responsive extension available, but that’s beyond the scope of this tutorial.
With the CSS in place, let’s see how the project looks. We won’t see a big difference yet because we haven’t initialized the libraries:
The JavaScript
Now for the JavaScript window in our pen. When the DOM is ready, the
init
function is executed; this function triggers other sub-functions:function init() { const table = $("#dt-table").DataTable(); const tableData = getTableData(table); createHighcharts(tableData); setTableEvents(table); }As you’ll see, each of these sub-functions accomplishes a certain task.
Initializing DataTables
The first step is to convert our table into a “DataTables” table. We can do this with just one line of code: $(“#dt-table”).DataTable();
If we now look at the table, we’ll notice that it has adopted the capabilities of a DataTables table, ie: we can sort it, search it, and so on. Have a play with it in the following demo:
Now, as you can see, DataTables applies a default sorting to the table. If needed, we can customize this behavior.
Extracting Table Data
The next step is to grab the table data and build the chart. We don’t want all the table data. In fact, if you look back at the finished version of our demo, you’ll notice that the chart only contains data from the first three columns (Country, Population, Density).
With that in mind, in order to retrieve the required data, we’ll take advantage of the DataTables API. The function responsible for this behavior is the following one:
function getTableData(table) { const dataArray = [], countryArray = [], populationArray = [], densityArray = []; // loop table rows table.rows({ search: "applied" }).every(function() { const data = this.data(); countryArray.push(data[0]); populationArray.push(parseInt(data[1].replace(/,/g, ""))); densityArray.push(parseInt(data[2].replace(/,/g, ""))); }); // store all data in dataArray dataArray.push(countryArray, populationArray, densityArray); return dataArray; }Inside this function, we iterate through the table rows and for each row, we grab the target column data and store them in the associated arrays. Finally, all those arrays are stored inside another array.
Here’s a quick visualization of the master (i.e.
dataArray
) array:Before moving on, it’s important to understand one thing. By default, the
getTableData
function should collect data from all the table rows. But then, if we search the table for something specific, only the rows that match should be collected and processed. To accomplish these things, we pass an argument to therows
function. Specifically, an object with thesearch: "applied"
property value.Again note that if we don’t pass this object, we’ll always collect data from all the table rows (test it). For more information about the properties that we can pass to this object, be sure to look at this page.
Building the Chart
Now that we have the desired data, we’re ready to build the chart. This will contain two nested charts, one Column chart and one Spline chart.
Here’s the corresponding function:
function createHighcharts(data) { Highcharts.setOptions({ lang: { thousandsSep: "," } }); Highcharts.chart("chart", { title: { text: "DataTables to Highcharts" }, subtitle: { text: "Data from worldometers.info" }, xAxis: [ { categories: data[0], labels: { rotation: -45 } } ], yAxis: [ { // first yaxis title: { text: "Population (2017)" } }, { // secondary yaxis title: { text: "Density (P/Km²)" }, min: 0, opposite: true } ], series: [ { name: "Population (2017)", color: "#0071A7", type: "column", data: data[1], tooltip: { valueSuffix: " M" } }, { name: "Density (P/Km²)", color: "#FF404E", type: "spline", data: data[2], yAxis: 1 } ], tooltip: { shared: true }, legend: { backgroundColor: "#ececec", shadow: true }, credits: { enabled: false }, noData: { style: { fontSize: "16px" } } }); }Don’t be overwhelmed by the code above! Without doubt the best way to understand how it works is to try it. Plus, you should definitely read the documentation. Anyhow, let’s briefly highlight the key concepts:
- The x-axis contains all the countries.
- We define two y-axes. The first one holds all the population values, while the second one includes all the available densities.
- If our chart doesn’t contain any data a message appears. Note that we’re able to customize the message text through the
lang
object.With the charts in place, let’s again look at our progress:
Synchronizing the Table and Charts
In the previous section, we built the chart based on the table data, but there’s still no full synchronization between the table and chart. To prove it, go back to the last demo and change the ordering (sorting) of the table, or search for something. You’ll notice that the chart doesn’t react to table changes.
To fix this, we’ll take advantage of the DataTables
draw
event. This event fires each time the table gets updated. So as soon as we modify the table we should recollect the table data and reconstruct the chart.Here’s the tricky thing though. The
draw
event also fires during the table pagination; there’s no reason to rebuild the chart during this process. Ideally, we should prevent this behavior. Thankfully, there’s thepage
event which helps us accomplish this task.Here’s the code that implements the desired functionality:
let draw = false; function setTableEvents(table) { // listen for page clicks table.on("page", () => { draw = true; }); // listen for updates and adjust the chart accordingly table.on("draw", () => { if (draw) { draw = false; } else { const tableData = getTableData(table); createHighcharts(tableData); } }); }Now that both table and chart are synchronized, if we make a “bad” search, we’ll see the following messages:
The final version of our project:
Browser Support
Both plugins have great browser support (DataTables support, Highcharts support), so you can expect that this demo will work well in all recent browsers.
Again keep in mind that this demo isn’t optimized for small screens.
Lastly, as usual, we use Babel to compile the ES6 code down to ES5.
Conclusion
That’s it folks! We managed to visualize our data by combining two popular and powerful JavaScript libraries.
Now that you’re familiar with the process, go ahead and elaborate the functionality of the final demo. For instance, try to add custom filters to the table.
As always, if you have any questions or if there’s anything else you would like to see as a next step to this tutorial, let me know in the comments below.
Powered by WPeMatico