Skip to content

Instantly share code, notes, and snippets.

@crevoisiersabine
Last active May 2, 2019 08:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save crevoisiersabine/34cef262b6632d82e4a6 to your computer and use it in GitHub Desktop.
Save crevoisiersabine/34cef262b6632d82e4a6 to your computer and use it in GitHub Desktop.
Dynamic Table (part of CS171 Harvard Visualisation assignment)
<!DOCTYPE html>
<html>
<head>
<style>
table {
/*To avoid spaces between the cells*/
border-collapse: collapse;
/*To center it*/
margin-left:auto;
margin-right:auto;
}
td, th, caption {
/*To add solid lines in the cells*/
border: solid 1px black;
/*To add spacing between the text in the cells and the borders*/
padding: 10px;
text-align: center;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
//Define click counter
var click = 1;
// Add a hard coded headline, dynamically with D3 (from http://www.bls.gov/web/laus/laumstrk.htm)
var H1 = d3.select("body").append("h1").text("Unemployment Rates for States");
// Center header
H1.style({"text-align": "center"});
// This piece of code prints the output of d3.text to the console to check what it looks like
// d3.text("unemp_states_us_nov_2013.tsv", function(error, data) {
// console.log(d3.tsv.parseRows(data));
// });
// d3.text reads the url file and returns a text file that can then be parsed as a tsv file
// We cannot use d3.tsv straight away as this skips the header line which we want to visualise
d3.text("unemp_states_us_nov_2013.tsv", function(error, data){
// Creates the DOM elements table, tbody and thead
var TABLE = d3.select("body").append("table"),
caption = TABLE.append("caption"),
thead = TABLE.append("thead")
tbody = TABLE.append("tbody");
// Another way to add the caption without using the caption tag
// var caption = d3.select("thead").append("tr").append("th").attr("colspan", "3");
// Add a hard coded caption, dynamically with D3 (from http://www.bls.gov/web/laus/laumstrk.html
caption.html("<b>Unemployment Rates for States<br>Monthly Rankings<br>Seasonally Adjusted<br>Apr. 2014<sup>p</sup></b>");
var parsedData = d3.tsv.parseRows(data);
// .shift() removes the first element of the array parsedData and modifies the array.
// It returns this element and I store it into the variable called header
var header = parsedData.shift();
// Create the rows to the table based on the number of entries in the parsedData array
var rows = tbody.selectAll("tr")
.data(parsedData, String)
.enter()
.append("tr")
.on("mouseover", function mouseOver(d, i){
d3.select(this).style({"background-color": "yellow"});
})
.on("mouseout", mouseOut);
// Append a child row element "tr" to <thead> where to put the header
var rowsH = thead.append("tr");
// Create the cells with <td> for each element of the array of array parsedData
var cells = rows.selectAll("td")
.data(function(c, i) {
return parsedData[i];
})
.enter()
.append("td")
.text(function(d) { return d; })
// Adding a class element corresponding to the array position for column highlight on mouseover
.attr('class', function(d, i){ return "col_" + i; });
// Creates the header cell
var cellsH = rowsH.selectAll("th")
.data(header)
.enter()
.append("th")
.text(function(d) { return d; })
.attr('class', function(d, i){ return "colH_" + i; });
//The code below creates the header rows without requiring <thead>
// var headerCell = rows.select("th")
// .data(header)
// .enter()
// .insert("th", ":first-child")
// .order()
// .text(function(d) { return d; });
// Make the table zebra coloured
tbody.selectAll("tr").style("background-color", function(d, i){
if(i%2 == 0){
return "#D8D8D8";
}
})
//Dimensions for the svg where rectange of last column will fit
var height = 15;
var width = 30;
//Add an extra column to the table
rows.insert("td").attr("class", "col_3").append("svg")
.attr("width", width)
.attr("height", height)
.append("rect")
.attr("height", height)
.attr("width", function(d) { return parseFloat(d[0]); });
//Add a header to the extra column, add a class and sorting functionality
rowsH.insert("th").text("Chart").attr("class", "colH_3");
//Bind the width of the rect bar to the rows DOM element (?no need to do that actually as for sorting I use the value of rate instead of rectangle length...)
d3.selectAll("rect").each(function(d, i){
rows.data()[i].push(d3.select(this).attr("width"));
});
// Binds this last header cell to cellsH for clicking and hovering events
cellsH[0].push(d3.select("th.colH_3")[0][0]);
cellsH.on("click", toggle)
.on("mouseover", mouse);
// Add this last column to the cells to enable hovering over last column as previous ones
for(j = 0; j < cells.length; j++){
cells[0, j].push(d3.selectAll("td.col_3")[0][j]);
}
cells.on("mouseover", function mouseOver(d, i){
d3.selectAll('td.col_' + i)
.style('background-color', 'yellow');
})
.on("mouseout", function mouseOver(d, i){
if (i!=2){
d3.selectAll('td.col_' + i)
.style('background-color', null);
}
else{
d3.selectAll('td.col_2').style("background-color", function(d, i){
return colorScale(i);
});
}
});
//After mouseover, return the cells to their original colour
function mouseOut(d, i){
// tbody.selectAll("td").style("background-color", "white");
tbody.selectAll("tr").style("background-color", function(d, i){
if(i%2 == 0){
return "#D8D8D8";
}
else{
return "white";
}
})
}
//On load make the table sorted by rate in increasing order
tbody.selectAll("tr").sort(function(a, b) {
return (d3.ascending(a[2], b[2]) || d3.ascending(a[1], b[1]));
});
function toggle(d, i){
increasing(i, click);
if (click == 1){
if(i != 0) {
d3.select(".colH_" + i).style("cursor", "s-resize");
}
}
else{
if(i != 0) {
d3.select(".colH_" + i).style("cursor", "n-resize");
}
}
click*=(-1);
}
function increasing(i, click){
if (i == 1){
tbody.selectAll("tr").sort(function(a, b) {
return click*d3.ascending(a[i], b[i]);
});
}
else if (i == 2){
tbody.selectAll("tr").sort(function(a, b) {
//The statement below will only evaluate the statement after the or is the first statement returns 0 (ie. a tie)
return click*(d3.ascending(a[i], b[i]) || d3.ascending(a[i-1], b[i-1]));
});
}
else if (i == 3){
tbody.selectAll("tr").sort(function(a, b) {
return click*d3.ascending(a[i-1], b[i-1]);
});
}
}
//This function causes the cursor to change on hover as per the assignment, however I prefer that the cursor changes upon click, without requiring to hover out of the cell and back in to see the change. Therefore the code is copied into the on.click function. However this is kept because it is nice to get the cursor to change on the first instance prior to any click.
function mouse(d, i){
if(click == 1){
if(i != 0) {
d3.select(".colH_" + i).style("cursor", "s-resize");
}
}
else{
if(i != 0) {
d3.select(".colH_" + i).style("cursor", "n-resize");
}
}
}
// Visually encode the table as per rate value
d3.selectAll('td.col_2').style("background-color", function(d, i){
return colorScale(i);
});
function colorScale(i){
var color = d3.scale.linear()
.domain([0, tbody.selectAll("tr")[0].length-1])
.interpolate(d3.interpolateRgb)
.range(["orangered", "silver"]);
return color(i);
}
});
</script>
</body>
</html>
Rank State Rate
1 NORTH DAKOTA 2.6
2 SOUTH DAKOTA 3.6
3 NEBRASKA 3.7
4 UTAH 4.3
5 HAWAII 4.4
5 IOWA 4.4
5 VERMONT 4.4
5 WYOMING 4.4
9 MINNESOTA 4.6
10 KANSAS 5.1
10 NEW HAMPSHIRE 5.1
12 MONTANA 5.2
13 OKLAHOMA 5.4
13 VIRGINIA 5.4
15 IDAHO 6.1
15 MISSOURI 6.1
15 TEXAS 6.1
15 WEST VIRGINIA 6.1
19 ALABAMA 6.2
20 LOUISIANA 6.3
20 WISCONSIN 6.3
22 FLORIDA 6.4
22 MAINE 6.4
22 MARYLAND 6.4
22 NEW MEXICO 6.4
26 ALASKA 6.5
26 COLORADO 6.5
26 DELAWARE 6.5
29 WASHINGTON 6.8
30 MASSACHUSETTS 7.1
30 SOUTH CAROLINA 7.1
32 INDIANA 7.3
32 OREGON 7.3
32 PENNSYLVANIA 7.3
35 NEW YORK 7.4
35 NORTH CAROLINA 7.4
35 OHIO 7.4
38 ARKANSAS 7.5
39 CONNECTICUT 7.6
40 GEORGIA 7.7
41 ARIZONA 7.8
41 NEW JERSEY 7.8
43 TENNESSEE 8.1
44 KENTUCKY 8.2
45 MISSISSIPPI 8.3
46 CALIFORNIA 8.5
47 DISTRICT OF COLUMBIA 8.6
48 ILLINOIS 8.7
49 MICHIGAN 8.8
50 NEVADA 9.0
50 RHODE ISLAND 9.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment