Implementing CSV and Excel Export Functionality for Highcharts
1. Chart Initialization
First, configure the basic Highcharts instance with the desired dataset. In this example, we visualize annual revenue trends across different product lines.
const salesChart = Highcharts.chart('chart-wrapper', {
chart: {
type: 'area',
zoomType: 'x'
},
title: {
text: 'Annual Revenue Growth'
},
subtitle: {
text: 'Source: Internal Sales Records'
},
xAxis: {
categories: ['2018', '2019', '2020', '2021', '2022', '2023'],
title: { text: 'Fiscal Year' }
},
yAxis: {
title: { text: 'Revenue (USD)' },
labels: { formatter: function () { return '$' + this.value / 1000 + 'k'; } }
},
plotOptions: {
area: { stacking: 'normal', lineColor: '#666666', lineWidth: 1, marker: { lineWidth: 1, lineColor: '#666666' } }
},
series: [{
name: 'Services',
data: [45000, 49000, 52000, 61000, 75000, 88000]
}, {
name: 'Hardware',
data: [30000, 35000, 32000, 40000, 42000, 55000]
}, {
name: 'Licensing',
data: [15000, 18000, 22000, 28000, 35000, 41000]
}]
});2. Implementing the Export Plugin
To enable data extraction, extend the Highcharts prototype. This script defines methods to convert the chart data into a 2D array, formats it as a CSV or HTML table string, and triggers a browser download using Blob objects.
(function(H) {
'use strict';
// Helper: Convert chart data to a 2D array structure
H.Chart.prototype.getDataMatrix = function() {
const xAxis = this.xAxis[0];
const rows = [];
const headers = [];
// 1. Extract Headers
const xLabel = xAxis.options.title && xAxis.options.title.text;
headers.push(xLabel || (xAxis.isDatetimeAxis ? 'DateTime' : 'Category'));
this.series.forEach(series => {
if (series.visible && series.options.includeInCSVExport !== false) {
headers.push(series.name);
}
});
rows.push(headers);
// 2. Extract Data Points
// Assuming all series share the same X axis points
if (this.series.length > 0) {
const baseSeries = this.series.find(s => s.visible && s.options.includeInCSVExport !== false) || this.series[0];
baseSeries.points.forEach(point => {
const currentRow = [];
// Add X value
if (xAxis.categories) {
currentRow.push(xAxis.categories[point.x]);
} else if (xAxis.isDatetimeAxis) {
currentRow.push(H.dateFormat('%Y-%m-%d %H:%M:%S', point.x));
} else {
currentRow.push(point.x);
}
// Add Y values for each visible series
this.series.forEach(series => {
if (series.visible && series.options.includeInCSVExport !== false) {
const pt = series.points.find(p => p.x === point.x);
currentRow.push(pt ? pt.y : '');
}
});
rows.push(currentRow);
});
}
return rows;
};
// Helper: Generate CSV String
H.Chart.prototype.getCSVString = function() {
const matrix = this.getDataMatrix();
const csvRows = matrix.map(row => row.map(val => {
const str = String(val);
// Quote strings containing commas or quotes
if (str.indexOf(',') > -1 || str.indexOf('"') > -1) {
return `"${str.replace(/"/g, '""')}"`;
}
return str;
}).join(','));
return csvRows.join('\r\n');
};
// Helper: Generate HTML Table String (for Excel)
H.Chart.prototype.getHTMLTable = function() {
const matrix = this.getDataMatrix();
let html = '';
// Header
matrix[0].forEach(cell => html += `${cell} `);
html += ' ';
// Body
for (let i = 1; i < matrix.length; i++) {
html += '';
matrix[i].forEach(cell => html += `${cell} `);
html += ' ';
}
html += '
';
return html;
};
// Function to handle file download
const initiateDownload = function(url, filename) {
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = filename;
anchor.style.display = 'none';
document.body.appendChild(anchor);
anchor.click();
setTimeout(() => {
document.body.removeChild(anchor);
}, 100);
};
// Export to CSV
H.Chart.prototype.saveAsCSV = function() {
const csv = this.getCSVString();
const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' }); // Add BOM for Excel
const url = URL.createObjectURL(blob);
const filename = (this.title.textStr || 'export').replace(/ /g, '_') + '.csv';
initiateDownload(url, filename);
};
// Export to XLS (HTML-based)
H.Chart.prototype.saveAsExcel = function() {
const htmlTable = this.getHTMLTable();
const excelTemplate = `
${htmlTable}
`;
const blob = new Blob([excelTemplate], { type: 'application/vnd.ms-excel' });
const url = URL.createObjectURL(blob);
const filename = (this.title.textStr || 'export').replace(/ /g, '_') + '.xls';
initiateDownload(url, filename);
};
// Add buttons to the default exporting menu
if (H.getOptions().exporting) {
H.getOptions().exporting.buttons.contextButton.menuItems.push({
textKey: 'downloadCSV',
onclick: function() { this.saveAsCSV(); }
}, {
textKey: 'downloadXLS',
onclick: function() { this.saveAsExcel(); }
});
}
}(Highcharts));3. Triggering the Export
Bind the export methods to UI elements using standard DOM event listeners.
document.getElementById('export-csv-btn').addEventListener('click', function() {
if (salesChart) {
salesChart.saveAsCSV();
}
});
document.getElementById('export-xls-btn').addEventListener('click', function() {
if (salesChart) {
salesChart.saveAsExcel();
}
});