Generate a .csv from an HTML table on the client side

Have you ever had someone say, "This page is great! Can I get the data and use it in Excel?"

Sometimes, the beautiful data displayed is the result of complex calculations and logic which make adding a .csv or .xlsx export difficult.

As I was looking at the page today, I realized the easiest way to create a .csv might be to extract the data out of the DOM. This had the added advantage of requiring the least amount of development.

The key HTML5 features that can be leveraged for this are Data URIs and the download attribute of the anchor tag.

Data URIs allow you to embed content into a tags. In this example, the data URI is: "data:text/csv;base64,(data here)". This indicates that the data is text, containing CSV and encoded with base64.

The download attribute tells the browser to download the content associated with the link rather than navigating to it.

This code is written in plain JavaScript, no jQuery or other library. It queries the HTML and extracts the values in the table. Multi-column cells are padded to ensure the .csv content aligns to the table. Anything that isn't numeric is enclosed in double-quotes with any embedded double-quotes escaped.

I used two loops - the first to get the data from the DOM and the second to create the .csv. It could probably be done in a single loop as well.

The HTML for the link tag is:

<a id="export-link" href="" download="nifty.csv">Export to CSV</a>

  
 /* Create a .csv of the table */
                
// Get all the rows
var trs = document.querySelectorAll("tr");
 // Declare variables
var i,l,j,k,tds,cs,m,n;
var rows = [];
var cells;
                
// Loop through all the rows
l = trs.length;
for (i = 0; i < l; i++) {
  // Get all the cells on the row
  tds = trs[i].querySelectorAll("th,td");
  k = tds.length;
    if (k > 0) {
      cells = [];
        // Loop through all the cells (including headers)
        for (j = 0; j < k; j++) {
          cells.push(tds[j].textContent);
          // Check if this cell has a colspan
          cs = tds[j].colSpan;
          m = cs - 1;
          // Pad the row to ensure everything lines up
          for (n = 0; n < m; n++) {
            cells.push("");
          }
        }
        // Add the cells as a row
      rows.push(cells);
    }
}
// At this point, rows is a two dimensional array with everything from the table

// Create a data-uri of the CSV content
var base64encode, csv = "", values;

// Loop through all the rows
l = rows.length;
for (i = 0; i < l; i++) {
   // Loop through all the cells in the row
   k = rows[i].length;
   values = [];
   for(j = 0; j < k; j++) {
     value = rows[i][j];
     // If the value is not a number, enclose it in quotes
     if (isNaN(parseFloat(value,10))) {
       value = '"' + value.replace(/"/,'\\\"') + '"';
     }
     values.push(value);               }
     // Join the cells into a CSV row
     csv += values.join(",") + "\n";
   }
}
// Create the data-uri
base64encode = "data:text/csv;base64," + btoa(csv);
// Update the link
document.getElementById("export-link").href = base64encode;

This post courtesy of Game Creek Video