Thursday 1st October, 2020
By Sandy Galloway

Highcharts Integration

Recently, we've had some feedback asking about displaying information from a DataTable in a graphical format. This is quite possible to do using our API and a charting library. There are many available, but in this blog post we will use the ever popular Highcharts showing how it might be integrated into your DataTables projects.

Here is an example of the type of integration we will create there. Note that as the filter the table, the chart is redraw to reflect the filtered data:

NamePositionOfficeSalary
Tiger NixonSystem ArchitectEdinburgh$320,800
Garrett WintersAccountantTokyo$170,750
Ashton CoxJunior Technical AuthorSan Francisco$86,000
Cedric KellySenior Javascript DeveloperEdinburgh$433,060
Airi SatouAccountantTokyo$162,700
Brielle WilliamsonIntegration SpecialistNew York$372,000
Herrod ChandlerSales AssistantSan Francisco$137,500
Rhona DavidsonIntegration SpecialistTokyo$327,900
Colleen HurstJavascript DeveloperSan Francisco$205,500
Sonya FrostSoftware EngineerEdinburgh$103,600
Jena GainesOffice ManagerLondon$90,560
Quinn FlynnSupport LeadEdinburgh$342,000
Charde MarshallRegional DirectorSan Francisco$470,600
Haley KennedySenior Marketing DesignerLondon$313,500
Tatyana FitzpatrickRegional DirectorLondon$385,750
Michael SilvaMarketing DesignerLondon$198,500
Paul ByrdChief Financial Officer (CFO)New York$725,000
Gloria LittleSystems AdministratorNew York$237,500
Bradley GreerSoftware EngineerLondon$132,000
Dai RiosPersonnel LeadEdinburgh$217,500
Jenette CaldwellDevelopment LeadNew York$345,000
Yuri BerryChief Marketing Officer (CMO)New York$675,000
Caesar VancePre-Sales SupportNew York$106,450
Doris WilderSales AssistantSydney$85,600
Angelica RamosChief Executive Officer (CEO)London$1,200,000
Gavin JoyceDeveloperEdinburgh$92,575
Jennifer ChangRegional DirectorSingapore$357,650
Brenden WagnerSoftware EngineerSan Francisco$206,850
Fiona GreenChief Operating Officer (COO)San Francisco$850,000
Shou ItouRegional MarketingTokyo$163,000
Michelle HouseIntegration SpecialistSydney$95,400
Suki BurksDeveloperLondon$114,500
Prescott BartlettTechnical AuthorLondon$145,000
Gavin CortezTeam LeaderSan Francisco$235,500
Martena MccrayPost-Sales supportEdinburgh$324,050
Unity ButlerMarketing DesignerSan Francisco$85,675
Howard HatfieldOffice ManagerSan Francisco$164,500
Hope FuentesSecretarySan Francisco$109,850
Vivian HarrellFinancial ControllerSan Francisco$452,500
Timothy MooneyOffice ManagerLondon$136,200
Jackson BradshawDirectorNew York$645,750
Olivia LiangSupport EngineerSingapore$234,500
Bruno NashSoftware EngineerLondon$163,500
Sakura YamamotoSupport EngineerTokyo$139,575
Thor WaltonDeveloperNew York$98,540
Finn CamachoSupport EngineerSan Francisco$87,500
Serge BaldwinData CoordinatorSingapore$138,575
Zenaida FrankSoftware EngineerNew York$125,250
Zorita SerranoSoftware EngineerSan Francisco$115,000
Jennifer AcostaJunior Javascript DeveloperEdinburgh$75,650
Cara StevensSales AssistantNew York$145,600
Hermione ButlerRegional DirectorLondon$356,250
Lael GreerSystems AdministratorLondon$103,500
Jonas AlexanderDeveloperSan Francisco$86,500
Shad DeckerRegional DirectorEdinburgh$183,000
Michael BruceJavascript DeveloperSingapore$183,000
Donna SniderCustomer SupportNew York$112,000

In this post I'm going to walkthrough the process of linking up DataTables with Highcharts. I'll cover:

  • Static charting
  • Asynchronous charting
  • Charting with data processing

Static Charting

Highcharts offers a wide range of different graphs and charts that are supported. I'm going to use pie charts and column graphs in this post.

A very basic pie chart initialisation is shown below:

var myChart = Highcharts.chart('container', {
    chart: {
        type: 'pie',
    },
    title: {
        text: 'Staff Count Per Office',
    },
    series: [
        {
            data: countMap,
        },
    ],
});

The config is fairly self explanatory. We are setting the chart.type to be pie, for a pie chart and the title.text to Staff Count Per Office. The series array is being set to contain one object that contains the data required for the series.

Now all that is left to do is to collect the data from the DataTable. To do this we simply iterate through the values of the desired column, retrieved by the column().data() method, incrementing the values as we go. The resulting binned data is then converted to a array of objects for Highcharts to use.

var table = $('#example').DataTable();
var counts = {};

// Count the number of entries for each office
table
    .column(2, { search: 'applied' })
    .data()
    .each(function (val) {
        if (counts[val]) {
            counts[val] += 1;
        } else {
            counts[val] = 1;
        }
    });

// And map it to the format highcharts uses
var countMap = $.map(counts, function (val, key) {
    return {
        name: key,
        y: val,
    };
});

An example of the table and chart generated bu this code is shown below. There is also a live version available so that you can modify and experiment with it.

NamePositionOfficeSalary
Tiger NixonSystem ArchitectEdinburgh$320,800
Garrett WintersAccountantTokyo$170,750
Ashton CoxJunior Technical AuthorSan Francisco$86,000
Cedric KellySenior Javascript DeveloperEdinburgh$433,060
Airi SatouAccountantTokyo$162,700
Brielle WilliamsonIntegration SpecialistNew York$372,000
Herrod ChandlerSales AssistantSan Francisco$137,500
Rhona DavidsonIntegration SpecialistTokyo$327,900
Colleen HurstJavascript DeveloperSan Francisco$205,500
Sonya FrostSoftware EngineerEdinburgh$103,600
Jena GainesOffice ManagerLondon$90,560
Quinn FlynnSupport LeadEdinburgh$342,000
Charde MarshallRegional DirectorSan Francisco$470,600
Haley KennedySenior Marketing DesignerLondon$313,500
Tatyana FitzpatrickRegional DirectorLondon$385,750
Michael SilvaMarketing DesignerLondon$198,500
Paul ByrdChief Financial Officer (CFO)New York$725,000
Gloria LittleSystems AdministratorNew York$237,500
Bradley GreerSoftware EngineerLondon$132,000
Dai RiosPersonnel LeadEdinburgh$217,500
Jenette CaldwellDevelopment LeadNew York$345,000
Yuri BerryChief Marketing Officer (CMO)New York$675,000
Caesar VancePre-Sales SupportNew York$106,450
Doris WilderSales AssistantSydney$85,600
Angelica RamosChief Executive Officer (CEO)London$1,200,000
Gavin JoyceDeveloperEdinburgh$92,575
Jennifer ChangRegional DirectorSingapore$357,650
Brenden WagnerSoftware EngineerSan Francisco$206,850
Fiona GreenChief Operating Officer (COO)San Francisco$850,000
Shou ItouRegional MarketingTokyo$163,000
Michelle HouseIntegration SpecialistSydney$95,400
Suki BurksDeveloperLondon$114,500
Prescott BartlettTechnical AuthorLondon$145,000
Gavin CortezTeam LeaderSan Francisco$235,500
Martena MccrayPost-Sales supportEdinburgh$324,050
Unity ButlerMarketing DesignerSan Francisco$85,675
Howard HatfieldOffice ManagerSan Francisco$164,500
Hope FuentesSecretarySan Francisco$109,850
Vivian HarrellFinancial ControllerSan Francisco$452,500
Timothy MooneyOffice ManagerLondon$136,200
Jackson BradshawDirectorNew York$645,750
Olivia LiangSupport EngineerSingapore$234,500
Bruno NashSoftware EngineerLondon$163,500
Sakura YamamotoSupport EngineerTokyo$139,575
Thor WaltonDeveloperNew York$98,540
Finn CamachoSupport EngineerSan Francisco$87,500
Serge BaldwinData CoordinatorSingapore$138,575
Zenaida FrankSoftware EngineerNew York$125,250
Zorita SerranoSoftware EngineerSan Francisco$115,000
Jennifer AcostaJunior Javascript DeveloperEdinburgh$75,650
Cara StevensSales AssistantNew York$145,600
Hermione ButlerRegional DirectorLondon$356,250
Lael GreerSystems AdministratorLondon$103,500
Jonas AlexanderDeveloperSan Francisco$86,500
Shad DeckerRegional DirectorEdinburgh$183,000
Michael BruceJavascript DeveloperSingapore$183,000
Donna SniderCustomer SupportNew York$112,000

Now that is pretty cool, but wouldn't it be better if the graph could reflect any filtering that is applied to it? Keep reading...

Asynchronous Charting

To achieve this we are going to have to make a couple of changes to the code above. We are going to take the office counting out of the $(document).ready(...) listener and put it in it's own function, replacing it with a call to our new function. The function should look something like this:

function chartData(table) {
    var counts = {};

    // Count the number of entries for each office
    table
        .column(2, { search: 'applied' })
        .data()
        .each(function (val) {
            if (counts[val]) {
                counts[val] += 1;
            } else {
                counts[val] = 1;
            }
        });

    // And map it to the format highcharts uses
    return $.map(counts, function (val, key) {
        return {
            name: key,
            y: val,
        };
    });
}

Using this approach means that positions fall off of the chart as filtering takes place, leading to a neater charting experience.

Now all that is left is to add a listener for when the filtering occurs to tell the chart to update with new data - for this we use draw.

table.on('draw', function () {
    // Set the data for the first series to be the map returned from the chartData function
    myChart.series[0].setData(chartData(table));
});

An example of this code is given below, and as before a live version showing the full, running code is available.

NamePositionOfficeSalary
Tiger NixonSystem ArchitectEdinburgh$320,800
Garrett WintersAccountantTokyo$170,750
Ashton CoxJunior Technical AuthorSan Francisco$86,000
Cedric KellySenior Javascript DeveloperEdinburgh$433,060
Airi SatouAccountantTokyo$162,700
Brielle WilliamsonIntegration SpecialistNew York$372,000
Herrod ChandlerSales AssistantSan Francisco$137,500
Rhona DavidsonIntegration SpecialistTokyo$327,900
Colleen HurstJavascript DeveloperSan Francisco$205,500
Sonya FrostSoftware EngineerEdinburgh$103,600
Jena GainesOffice ManagerLondon$90,560
Quinn FlynnSupport LeadEdinburgh$342,000
Charde MarshallRegional DirectorSan Francisco$470,600
Haley KennedySenior Marketing DesignerLondon$313,500
Tatyana FitzpatrickRegional DirectorLondon$385,750
Michael SilvaMarketing DesignerLondon$198,500
Paul ByrdChief Financial Officer (CFO)New York$725,000
Gloria LittleSystems AdministratorNew York$237,500
Bradley GreerSoftware EngineerLondon$132,000
Dai RiosPersonnel LeadEdinburgh$217,500
Jenette CaldwellDevelopment LeadNew York$345,000
Yuri BerryChief Marketing Officer (CMO)New York$675,000
Caesar VancePre-Sales SupportNew York$106,450
Doris WilderSales AssistantSydney$85,600
Angelica RamosChief Executive Officer (CEO)London$1,200,000
Gavin JoyceDeveloperEdinburgh$92,575
Jennifer ChangRegional DirectorSingapore$357,650
Brenden WagnerSoftware EngineerSan Francisco$206,850
Fiona GreenChief Operating Officer (COO)San Francisco$850,000
Shou ItouRegional MarketingTokyo$163,000
Michelle HouseIntegration SpecialistSydney$95,400
Suki BurksDeveloperLondon$114,500
Prescott BartlettTechnical AuthorLondon$145,000
Gavin CortezTeam LeaderSan Francisco$235,500
Martena MccrayPost-Sales supportEdinburgh$324,050
Unity ButlerMarketing DesignerSan Francisco$85,675
Howard HatfieldOffice ManagerSan Francisco$164,500
Hope FuentesSecretarySan Francisco$109,850
Vivian HarrellFinancial ControllerSan Francisco$452,500
Timothy MooneyOffice ManagerLondon$136,200
Jackson BradshawDirectorNew York$645,750
Olivia LiangSupport EngineerSingapore$234,500
Bruno NashSoftware EngineerLondon$163,500
Sakura YamamotoSupport EngineerTokyo$139,575
Thor WaltonDeveloperNew York$98,540
Finn CamachoSupport EngineerSan Francisco$87,500
Serge BaldwinData CoordinatorSingapore$138,575
Zenaida FrankSoftware EngineerNew York$125,250
Zorita SerranoSoftware EngineerSan Francisco$115,000
Jennifer AcostaJunior Javascript DeveloperEdinburgh$75,650
Cara StevensSales AssistantNew York$145,600
Hermione ButlerRegional DirectorLondon$356,250
Lael GreerSystems AdministratorLondon$103,500
Jonas AlexanderDeveloperSan Francisco$86,500
Shad DeckerRegional DirectorEdinburgh$183,000
Michael BruceJavascript DeveloperSingapore$183,000
Donna SniderCustomer SupportNew York$112,000

This not only works with DataTables' global filter input, but also with extensions such as SearchPanes, which you can see in the following example. A slight difference here is that we are charting the Position column. Again, I've linked a live version.

NamePositionOfficeSalary
Tiger NixonSystem ArchitectEdinburgh$320,800
Garrett WintersAccountantTokyo$170,750
Ashton CoxJunior Technical AuthorSan Francisco$86,000
Cedric KellySenior Javascript DeveloperEdinburgh$433,060
Airi SatouAccountantTokyo$162,700
Brielle WilliamsonIntegration SpecialistNew York$372,000
Herrod ChandlerSales AssistantSan Francisco$137,500
Rhona DavidsonIntegration SpecialistTokyo$327,900
Colleen HurstJavascript DeveloperSan Francisco$205,500
Sonya FrostSoftware EngineerEdinburgh$103,600
Jena GainesOffice ManagerLondon$90,560
Quinn FlynnSupport LeadEdinburgh$342,000
Charde MarshallRegional DirectorSan Francisco$470,600
Haley KennedySenior Marketing DesignerLondon$313,500
Tatyana FitzpatrickRegional DirectorLondon$385,750
Michael SilvaMarketing DesignerLondon$198,500
Paul ByrdChief Financial Officer (CFO)New York$725,000
Gloria LittleSystems AdministratorNew York$237,500
Bradley GreerSoftware EngineerLondon$132,000
Dai RiosPersonnel LeadEdinburgh$217,500
Jenette CaldwellDevelopment LeadNew York$345,000
Yuri BerryChief Marketing Officer (CMO)New York$675,000
Caesar VancePre-Sales SupportNew York$106,450
Doris WilderSales AssistantSydney$85,600
Angelica RamosChief Executive Officer (CEO)London$1,200,000
Gavin JoyceDeveloperEdinburgh$92,575
Jennifer ChangRegional DirectorSingapore$357,650
Brenden WagnerSoftware EngineerSan Francisco$206,850
Fiona GreenChief Operating Officer (COO)San Francisco$850,000
Shou ItouRegional MarketingTokyo$163,000
Michelle HouseIntegration SpecialistSydney$95,400
Suki BurksDeveloperLondon$114,500
Prescott BartlettTechnical AuthorLondon$145,000
Gavin CortezTeam LeaderSan Francisco$235,500
Martena MccrayPost-Sales supportEdinburgh$324,050
Unity ButlerMarketing DesignerSan Francisco$85,675
Howard HatfieldOffice ManagerSan Francisco$164,500
Hope FuentesSecretarySan Francisco$109,850
Vivian HarrellFinancial ControllerSan Francisco$452,500
Timothy MooneyOffice ManagerLondon$136,200
Jackson BradshawDirectorNew York$645,750
Olivia LiangSupport EngineerSingapore$234,500
Bruno NashSoftware EngineerLondon$163,500
Sakura YamamotoSupport EngineerTokyo$139,575
Thor WaltonDeveloperNew York$98,540
Finn CamachoSupport EngineerSan Francisco$87,500
Serge BaldwinData CoordinatorSingapore$138,575
Zenaida FrankSoftware EngineerNew York$125,250
Zorita SerranoSoftware EngineerSan Francisco$115,000
Jennifer AcostaJunior Javascript DeveloperEdinburgh$75,650
Cara StevensSales AssistantNew York$145,600
Hermione ButlerRegional DirectorLondon$356,250
Lael GreerSystems AdministratorLondon$103,500
Jonas AlexanderDeveloperSan Francisco$86,500
Shad DeckerRegional DirectorEdinburgh$183,000
Michael BruceJavascript DeveloperSingapore$183,000
Donna SniderCustomer SupportNew York$112,000

Charting with Data Processing

Of course it is also possible to do some more processing with the data from the DataTable. The example below is creating a column graph, showing the average salaries of the employees in the example table, by office. And of course, here is a link to a live version.

NamePositionOfficeSalary
Tiger NixonSystem ArchitectEdinburgh$320,800
Garrett WintersAccountantTokyo$170,750
Ashton CoxJunior Technical AuthorSan Francisco$86,000
Cedric KellySenior Javascript DeveloperEdinburgh$433,060
Airi SatouAccountantTokyo$162,700
Brielle WilliamsonIntegration SpecialistNew York$372,000
Herrod ChandlerSales AssistantSan Francisco$137,500
Rhona DavidsonIntegration SpecialistTokyo$327,900
Colleen HurstJavascript DeveloperSan Francisco$205,500
Sonya FrostSoftware EngineerEdinburgh$103,600
Jena GainesOffice ManagerLondon$90,560
Quinn FlynnSupport LeadEdinburgh$342,000
Charde MarshallRegional DirectorSan Francisco$470,600
Haley KennedySenior Marketing DesignerLondon$313,500
Tatyana FitzpatrickRegional DirectorLondon$385,750
Michael SilvaMarketing DesignerLondon$198,500
Paul ByrdChief Financial Officer (CFO)New York$725,000
Gloria LittleSystems AdministratorNew York$237,500
Bradley GreerSoftware EngineerLondon$132,000
Dai RiosPersonnel LeadEdinburgh$217,500
Jenette CaldwellDevelopment LeadNew York$345,000
Yuri BerryChief Marketing Officer (CMO)New York$675,000
Caesar VancePre-Sales SupportNew York$106,450
Doris WilderSales AssistantSydney$85,600
Angelica RamosChief Executive Officer (CEO)London$1,200,000
Gavin JoyceDeveloperEdinburgh$92,575
Jennifer ChangRegional DirectorSingapore$357,650
Brenden WagnerSoftware EngineerSan Francisco$206,850
Fiona GreenChief Operating Officer (COO)San Francisco$850,000
Shou ItouRegional MarketingTokyo$163,000
Michelle HouseIntegration SpecialistSydney$95,400
Suki BurksDeveloperLondon$114,500
Prescott BartlettTechnical AuthorLondon$145,000
Gavin CortezTeam LeaderSan Francisco$235,500
Martena MccrayPost-Sales supportEdinburgh$324,050
Unity ButlerMarketing DesignerSan Francisco$85,675
Howard HatfieldOffice ManagerSan Francisco$164,500
Hope FuentesSecretarySan Francisco$109,850
Vivian HarrellFinancial ControllerSan Francisco$452,500
Timothy MooneyOffice ManagerLondon$136,200
Jackson BradshawDirectorNew York$645,750
Olivia LiangSupport EngineerSingapore$234,500
Bruno NashSoftware EngineerLondon$163,500
Sakura YamamotoSupport EngineerTokyo$139,575
Thor WaltonDeveloperNew York$98,540
Finn CamachoSupport EngineerSan Francisco$87,500
Serge BaldwinData CoordinatorSingapore$138,575
Zenaida FrankSoftware EngineerNew York$125,250
Zorita SerranoSoftware EngineerSan Francisco$115,000
Jennifer AcostaJunior Javascript DeveloperEdinburgh$75,650
Cara StevensSales AssistantNew York$145,600
Hermione ButlerRegional DirectorLondon$356,250
Lael GreerSystems AdministratorLondon$103,500
Jonas AlexanderDeveloperSan Francisco$86,500
Shad DeckerRegional DirectorEdinburgh$183,000
Michael BruceJavascript DeveloperSingapore$183,000
Donna SniderCustomer SupportNew York$112,000

The code for this example is very similar to the previous examples, with a couple of tweaks to the extra function that we created.

function getSalaries(table) {
    var salaryCounts = {};
    var salary = {};

    // Get the row indexes for the rows displayed under the current search
    var indexes = table.rows({ search: 'applied' }).indexes().toArray();

    // For each row, extract the office and add the salary to the array
    for (var i = 0; i < indexes.length; i++) {
        var office = table.cell(indexes[i], 2).data();
        if (salaryCounts[office] === undefined) {
            salaryCounts[office] = [
                +table
                    .cell(indexes[i], 3)
                    .data()
                    .replace(/[^0-9.]/g, ''),
            ];
        } else {
            salaryCounts[office].push(
                +table
                    .cell(indexes[i], 3)
                    .data()
                    .replace(/[^0-9.]/g, '')
            );
        }
    }

    // Extract the office names that are present in the table
    var keys = Object.keys(salaryCounts);

    // For each office work out the average salary
    for (var i = 0; i < keys.length; i++) {
        var length = salaryCounts[keys[i]].length;
        var total = salaryCounts[keys[i]].reduce((a, b) => a + b, 0);
        salary[keys[i]] = total / length;
    }

    return salary;
}

Here we have 2 objects, the first salary object is going to hold the final data that we need for a column graph, while salaryCounts is a temporary object that will allow us to perform some extra processing.

We also have to make a change to our series variable, the data is now the values of the salary array. We also need to declare an x and y axis. Putting all of this together gives us the following scipt, which is used for the example above.

$(document).ready(function () {
    var table = $('#example').DataTable();
    var salary = getSalaries(table);

    // Declare axis for the column graph
    var axis = {
        id: 'salary',
        min: 0,
        title: {
            text: 'Average Salary',
        },
    };

    // Declare inital series with the values from the getSalaries function
    var series = {
        name: 'Overall',
        data: Object.values(salary),
    };

    var myChart = Highcharts.chart('container', {
        chart: {
            type: 'column',
        },
        title: {
            text: 'Average Salary',
        },
        xAxis: {
            categories: Object.keys(salary),
        },
        yAxis: axis,
        series: [series],
    });

    // On draw, get updated salaries and refresh axis and series
    table.on('draw', function () {
        salary = getSalaries(table);
        myChart.axes[0].categories = Object.keys(salary);
        myChart.series[0].setData(Object.values(salary));
    });
});

function getSalaries(table) {
    var salaryCounts = {};
    var salary = {};

    // Get the row indexes for the rows displayed under the current search
    var indexes = table.rows({ search: 'applied' }).indexes().toArray();

    // For each row, extract the office and add the salary to the array
    for (var i = 0; i < indexes.length; i++) {
        var office = table.cell(indexes[i], 2).data();
        if (salaryCounts[office] === undefined) {
            salaryCounts[office] = [
                +table
                    .cell(indexes[i], 3)
                    .data()
                    .replace(/[^0-9.]/g, ''),
            ];
        } else {
            salaryCounts[office].push(
                +table
                    .cell(indexes[i], 3)
                    .data()
                    .replace(/[^0-9.]/g, '')
            );
        }
    }

    // Extract the office names that are present in the table
    var keys = Object.keys(salaryCounts);

    // For each office work out the average salary
    for (var i = 0; i < keys.length; i++) {
        var length = salaryCounts[keys[i]].length;
        var total = salaryCounts[keys[i]].reduce((a, b) => a + b, 0);
        salary[keys[i]] = total / length;
    }

    return salary;
}

As I've mentioned, Highcharts offers a wide range of different graphs, and by making use of the DataTables API it is easy to create dynamic charts and graphs to run alongside a DataTable.

Hopefully, this will help to add another dimension to your DataTables implementations.