Skip to content

Instantly share code, notes, and snippets.

@SHewitt95
Created February 9, 2016 01:52
Show Gist options
  • Save SHewitt95/8c7db588d7d0e07bb9fb to your computer and use it in GitHub Desktop.
Save SHewitt95/8c7db588d7d0e07bb9fb to your computer and use it in GitHub Desktop.
Week4: Sortable Heatmap table
Race or Ethnicity 2000 2012
White, non-Hispanic 38.7 42.1
Black, non-Hispanic 30.5 36.4
Hispanic 21.7 37.5
Asian, non-Hispanic 55.9 59.8
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>College Enrollment Percentages, 2000 versus 2012</title>
<style>
table {
/* border: 1px solid black;*/
}
td {
/*border-left: 1px solid black;
border-bottom: 1px solid black;
margin: 0;*/
padding: 1em;
}
table thead tr th {
padding: 0.5em;
}
table thead tr th:hover {
cursor: pointer;
}
table thead tr:nth-child(2n+1) {
background-color: #d3d3d3;
}
.container {
width: 75%;
display: block;
margin: 0 auto;
}
.pink {
background-color: pink;
}
</style>
</head>
<body>
<div class="container">
<h1>College Enrollment Percentages, 2000 versus 2012</h1>
<p>Source: Data.gov, U.S. Department of Commerce, Census Bureau, Current Population Survey</p>
<p>The data portrays the percentage of 18 to 24-year-olds per race/ethnicity who attend 2 or 4-year colleges. For example, in 2000, 21.7 percent of all Hispanics between age 18 and 24 attended college. By 2012, that percentage increased by about 16 percent to 37.5 percent.</p>
<div id="table"></div>
</div> <!-- End container -->
</body>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<!-- load the function file you need before you call it... -->
<script type="text/javascript" src="tabulate.js"></script>
<script type="text/javascript" src="stupidtable.js"></script>
<script type="text/javascript">
//Load in contents of CSV file, and do things to the data.
d3.csv("college_enrollment_data0.csv", function(error, data) {
if (error) {
console.log("Had an error loading file.");
}
data.forEach(function(d, i){
// now we add another data object value, a calculated value.
d["Change (Percentage)"] = (d["2012"] - d["2000"]).toPrecision(3);
});
//console.log(data);
data.sort(function(a,b) { return d3.ascending(a["Race or Ethnicity"], b["Race or Ethnicity"]);});
// the tabulate function wants the second argument to be your columns in your data that will be in the table.
// third argument is the element to put it into on the page
var regionTable = tabulate(data,
["Race or Ethnicity", "2000", "2012", "Change (Percentage)"],
"#table");
var cellsToHeatMap = d3.selectAll("#table table tbody tr td:nth-child(4)");
var colorScale = d3.scale.linear()
.domain([3.4, 15.8])
.range(["#98fb98", "orange"]);
cellsToHeatMap.style("background-color", function(d,i) {
return colorScale(+d.value);
})
/*cellsToHeatMap[0].forEach(function(cell) {
var value = +cell.innerText;
d3.select(cell)
.style("background-color", function(value) {
return colorScale(value);
});
})*/
/* var tableBox = d3.select("#table");
var table = tableBox.append("table"),
thead = table.append("thead"),
theadRow = thead.append("tr"),
tbody = table.append("tbody");
var headers = ["Race or Ethnicity", "Percentage in 2000", "Percentage in 2012", "Difference in Percentage"];
theadRow.selectAll("th")
.data(headers)
.enter()
.append("th")
.text(function(d, i) {return d;});
var rows = tbody.selectAll("tr")
.data(data) // Ties data to each row.
.enter()
.append("tr");
var cells = rows.selectAll("td")
.data(function(d) {return d;})
.enter()
.append("td")
.text(function(d,i) {return d;});*/
// Now we are adding attributes to the existing header cells here, for use
// in the jquery function below.
d3.selectAll("th")
.data(["string", "float", "float", "float"])
.attr("data-sort", function(d) {return d;});
// Now that the table exists - we can use it with JQuery functions!
$("table").stupidtable();
});
</script>
</html>
// Stupid jQuery table plugin.
(function($) {
$.fn.stupidtable = function(sortFns) {
return this.each(function() {
var $table = $(this);
sortFns = sortFns || {};
sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns);
$table.data('sortFns', sortFns);
$table.on("click.stupidtable", "thead th", function() {
$(this).stupidsort();
});
});
};
// Expects $("#mytable").stupidtable() to have already been called.
// Call on a table header.
$.fn.stupidsort = function(force_direction){
var $this_th = $(this);
var th_index = 0; // we'll increment this soon
var dir = $.fn.stupidtable.dir;
var $table = $this_th.closest("table");
var datatype = $this_th.data("sort") || null;
// No datatype? Nothing to do.
if (datatype === null) {
return;
}
// Account for colspans
$this_th.parents("tr").find("th").slice(0, $(this).index()).each(function() {
var cols = $(this).attr("colspan") || 1;
th_index += parseInt(cols,10);
});
var sort_dir;
if(arguments.length == 1){
sort_dir = force_direction;
}
else{
sort_dir = force_direction || $this_th.data("sort-default") || dir.ASC;
if ($this_th.data("sort-dir"))
sort_dir = $this_th.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC;
}
$table.trigger("beforetablesort", {column: th_index, direction: sort_dir});
// More reliable method of forcing a redraw
$table.css("display");
// Run sorting asynchronously on a timout to force browser redraw after
// `beforetablesort` callback. Also avoids locking up the browser too much.
setTimeout(function() {
// Gather the elements for this column
var column = [];
var sortFns = $table.data('sortFns');
var sortMethod = sortFns[datatype];
var trs = $table.children("tbody").children("tr");
// Extract the data for the column that needs to be sorted and pair it up
// with the TR itself into a tuple. This way sorting the values will
// incidentally sort the trs.
trs.each(function(index,tr) {
var $e = $(tr).children().eq(th_index);
var sort_val = $e.data("sort-value");
// Store and read from the .data cache for display text only sorts
// instead of looking through the DOM every time
if(typeof(sort_val) === "undefined"){
var txt = $e.text();
$e.data('sort-value', txt);
sort_val = txt;
}
column.push([sort_val, tr]);
});
// Sort by the data-order-by value
column.sort(function(a, b) { return sortMethod(a[0], b[0]); });
if (sort_dir != dir.ASC)
column.reverse();
// Replace the content of tbody with the sorted rows. Strangely
// enough, .append accomplishes this for us.
trs = $.map(column, function(kv) { return kv[1]; });
$table.children("tbody").append(trs);
// Reset siblings
$table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc");
$this_th.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir);
$table.trigger("aftertablesort", {column: th_index, direction: sort_dir});
$table.css("display");
}, 10);
return $this_th;
};
// Call on a sortable td to update its value in the sort. This should be the
// only mechanism used to update a cell's sort value. If your display value is
// different from your sort value, use jQuery's .text() or .html() to update
// the td contents, Assumes stupidtable has already been called for the table.
$.fn.updateSortVal = function(new_sort_val){
var $this_td = $(this);
if($this_td.is('[data-sort-value]')){
// For visual consistency with the .data cache
$this_td.attr('data-sort-value', new_sort_val);
}
$this_td.data("sort-value", new_sort_val);
return $this_td;
};
// ------------------------------------------------------------------
// Default settings
// ------------------------------------------------------------------
$.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"};
$.fn.stupidtable.default_sort_fns = {
"int": function(a, b) {
return parseInt(a, 10) - parseInt(b, 10);
},
"float": function(a, b) {
return parseFloat(a) - parseFloat(b);
},
"string": function(a, b) {
return a.localeCompare(b);
},
"string-ins": function(a, b) {
a = a.toLocaleLowerCase();
b = b.toLocaleLowerCase();
return a.localeCompare(b);
}
};
})(jQuery);
/*
Code by Shawn Allen (@shawnbot) repro'd in d3noob's book,
http://www.d3noob.org/2013/02/add-html-table-to-your-d3js-graph.html,
but with minor modification by Lynn.
*/
function tabulate(data, columns, id) {
var table = d3.select(id).append("table"),
thead = table.append("thead"),
tbody = table.append("tbody");
// append the header row
thead.append("tr")
.selectAll("th")
.data(columns)
.enter()
.append("th")
.text(function(column) { return column; });
// create a row for each object in the data
var rows = tbody.selectAll("tr")
.data(data)
.enter()
.append("tr");
// create a cell in each row for each column
// At this point, the rows have data associated.
// So the data function accesses it.
var cells = rows.selectAll("td")
.data(function(row) {
// he does it this way to guarantee you only use the
// values for the columns you provide.
return columns.map(function(column) {
// return a new object with a value set to the row's column value.
return { value: row[column] };
});
})
.enter()
.append("td")
.text(function(d) { return d.value; });
return table;
}
// render the table
// var peopleTable = tabulate(data, ["date", "close"]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment