Misericórdia Misericórdia - 16 days ago 9
HTML Question

Problems parsing CSV into HTML table with d3.js

I'm a newbie in terms of both JQuery and CSV parsing and I'm having a hard time trying to do it.

My CSV file is being generated and downloaded by Javascript from a HTML table in another page:

var table = document.getElementById("mytableid").innerHTML;
var data = table.replace(/<thead>/g, '')
.replace(/<\/thead>/g, '')
.replace(/<tbody>/g, '')
.replace(/<\/tbody>/g, '')
.replace(/<tr>/g, '')
.replace(/<\/tr>/g, '\r\n')
.replace(/<th>/g, '')
.replace(/<\/th>/g, ',')
.replace(/<td>/g, '')
.replace(/<\/td>/g, ',')
.replace(/\t/g, '');
.replace(/\n/g, '');
var mylink = document.createElement('a');
mylink.download = "mycsvfile.csv";
mylink.href = "data:application/csv," + escape(data);
mylink.click();


The download happens correctly. In the page where I want to display that CSV downloaded file, my script goes like this:

d3.text("mycsvfile.csv", function(datasetText) {
var parsedCSV = d3.csv.parseRows(datasetText);
var sampleHTML = d3.select("#divid")
.append("table")
.selectAll("tr")
.data(parsedCSV)
.enter().append("tr")
.selectAll("td")
.data(function(d){return d;})
.enter().append("td")
.text(function(d){return d;})
});


It shows the CSV table correctly except for the fact that there's one blank column (which does not exist in the original file) being displayed on the right, as if it was creating one last blank td in every tr. Also, it doesn't seem to have detected my th's: it shows them just as common td's.

Any thoughts on why that's happening and how I can solve it?
Thanks in advance!

Answer

There are some problems here, like this one:

Also, it doesn't seem to have detected my th's: it shows them just as common td's.

You don't have any th in your code. Besides that, the chaining is not correct.

That being said, I reckon the best way for appending a table using D3 is clearly defining who is the head, who is the body, who are the rows and who are the cells.

So, suppose you have this fake data:

foo,bar,baz
21,43,55
32,77,65
12,66,42
42,88,42

First step, let's define the components:

var body = d3.select("body");
var table = body.append('table')
var thead = table.append('thead')
var tbody = table.append('tbody');

For printing the table header, we get the header row in the CSV using Object.keys():

var headers = Object.keys(datasetText[0]);

And then we display it:

var head = thead.selectAll('th')
    .data(headers)
    .enter()
    .append('th')
    .text(function (d) { return d; });

Now, for the body of the table, we create the rows:

var rows = tbody.selectAll('tr')
    .data(datasetText)
    .enter()
    .append('tr');

And then we create the cells using Object.values():

var cells = rows.selectAll('td')
    .data(function (d) {
        return Object.values(d);
    })
    .enter()
    .append('td')
    .text(function (d) { return d; });

Please notice that Object.values() will not work with IE, Safari or Opera (only Chrome and Firefox at the moment). You can use a polyfill or an equivalent function, like a for...in loop:

    var cells = rows.selectAll('td')
    .data(function(d) {
        var arr = [];
        for (var i in d) {
            arr.push(d[i])
        }
        return arr;
    })
    .enter()
    .append('td')
    .text(function(d) { return d;});

This is the Plunker: https://plnkr.co/edit/kanrGplVfirTgc5Qm2lc?p=preview

And, to avoid plunker as an external link, here is the same code using SO snippet (I had to transform the CSV in the corresponding array of objects, because loading an CSV in SO snippets is complicated):

var datasetText = [{
    foo: 32,
    bar: 43,
    baz: 31
}, {
    foo: 12,
    bar: 88,
    baz: 11
}, {
    foo: 33,
    bar: 88,
    baz: 21
}, {
    foo: 17,
    bar: 33,
    baz: 42
}];

var body = d3.select("body");

var headers = Object.keys(datasetText[0]);

var table = body.append('table')
var thead = table.append('thead')
var tbody = table.append('tbody');

var head = thead.selectAll('th')
    .data(headers)
    .enter()
    .append('th')
    .text(function(d) {
        return d;
    });

var rows = tbody.selectAll('tr')
    .data(datasetText)
    .enter()
    .append('tr');

var cells = rows.selectAll('td')
    .data(function(d) {
        return Object.values(d);
    })
    .enter()
    .append('td')
    .text(function(d) {
        return d;
    });
.table-fill {
  background: white;
  border-radius:3px;
  border-collapse: collapse;
  height: 320px;
  margin: auto;
  max-width: 600px;
  padding:5px;
  width: 100%;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
  animation: float 5s infinite;
}
 
th {
  color:#D5DDE5;;
  background:#1b1e24;
  border-bottom:4px solid #9ea7af;
  border-right: 1px solid #343a45;
  font-size:23px;
  font-weight: 100;
  padding:10px;
  text-align:left;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
  vertical-align:middle;
}

th:first-child {
  border-top-left-radius:3px;
}
 
th:last-child {
  border-top-right-radius:3px;
  border-right:none;
}
  
tr {
  border-top: 1px solid #C1C3D1;
  border-bottom-: 1px solid #C1C3D1;
  color:#666B85;
  font-size:16px;
  font-weight:normal;
  text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
}
 
tr:hover td {
  background:#4E5066;
  color:#FFFFFF;
  border-top: 1px solid #22262e;
  border-bottom: 1px solid #22262e;
}
 
tr:first-child {
  border-top:none;
}

tr:last-child {
  border-bottom:none;
}
 
tr:nth-child(odd) td {
  background:#EBEBEB;
}
 
tr:nth-child(odd):hover td {
  background:#4E5066;
}

tr:last-child td:first-child {
  border-bottom-left-radius:3px;
}
 
tr:last-child td:last-child {
  border-bottom-right-radius:3px;
}
 
td {
  background:#FFFFFF;
  padding:10px;
  text-align:left;
  vertical-align:middle;
  font-weight:300;
  font-size:18px;
  text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.1);
  border-right: 1px solid #C1C3D1;
}

td:last-child {
  border-right: 0px;
}

th.text-left {
  text-align: left;
}

th.text-center {
  text-align: center;
}

th.text-right {
  text-align: right;
}

td.text-left {
  text-align: left;
}

td.text-center {
  text-align: center;
}

td.text-right {
  text-align: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>