Skip to content

Instantly share code, notes, and snippets.

@luluwuluying
Last active February 8, 2016 19:14
Show Gist options
  • Save luluwuluying/ace3699c70a2e3e7a2bb to your computer and use it in GitHub Desktop.
Save luluwuluying/ace3699c70a2e3e7a2bb to your computer and use it in GitHub Desktop.
Week4: Sortable Heatmap table
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CSV Data in a Table with D3</title>
</head>
<style>
body {
font-family: Arial, sans-serif;
font-size: 1em;
color: steelblue;
}
#table {
border-collapse: collapse;
padding: 10px;
}
#th,tr ,td{
padding: 15px;
text-align:center;
}
th {
background-color: steelblue;
color: white;
padding: 15px;
cursor:pointer;
}
tr{
background-color:#E6F5FF ;
}
</style>
<body>
<h1>My Table with Heatmap!</h1>
<p>This table shows how many visits and views in these social media in Jan 2016 </p>
<p>Source: https://www.archives.gov/social-media/reports/social-media-stats-fy-2016-1.pdf</p>
<div id="table"></div>
</body>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<!-- load the function file you need before you call it... -->
<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("Socialmedia.csv", function(error, myData) {
if (error) {
console.log("Had an error loading file.");
}
// We'll be using simpler data as values, not objects.
var myArray = [];
// this is a new variable, to make it easier to do a color scale.
// alternately, you could extract these values with a map function.
var allDifferences = [];
myData.forEach(function(d, i){
// now we add another data object value, a calculated value.
// a.Monthlyviews, b.Monthlyvisits
d.difference = d.Monthlyviews - d.Monthlyvisits;
// Add an array to the empty array with the values of each:
myArray.push([d.Tool, d.Monthlyviews, d.Monthlyvisits, d.difference]);
// this is just a convenience, another way would be to use a function to get the values in the d3 scale.
allDifferences.push(d.difference);
});
console.log(allDifferences);
var table = d3.select("#table").append("table");
var header = table.append("thead").append("tr");
// Made some objects to construct the header in code:
// The sort_type is for the Jquery sorting function.
var headerObjs = [
{ label: "Tool", sort_type: "string" },
{ label: "Monthlyviews", sort_type: "int" },
{ label: "Monthlyvisits", sort_type: "int" },
{ label: "Difference", sort_type: "int" },
];
header
.selectAll("th")
.data(headerObjs)
.enter()
.append("th")
.attr("data-sort", function (d) { return d.sort_type; })
.text(function(d) { return d.label; });
var tablebody = table.append("tbody");
rows = tablebody
.selectAll("tr")
.data(myArray)
.enter()
.append("tr");
// We built the rows using the nested array - now each row has its own array.
// let's talk about the scale - start at 0 or at lowest number?
console.log('Extent is ', d3.extent(allDifferences));
var colorScale = d3.scale.linear()
.domain(d3.extent(allDifferences))
.range(["#FFC0CB","#C71585"]);
cells = rows.selectAll("td")
// each row has data associated; we get it and enter it for the cells.
.data(function(d) {
return d;
})
.enter()
.append("td")
.style("background-color", function(d,i) {
// for the last element in the row, we color the background:
if (i === 3) {
return colorScale(d);
}
})
.text(function(d) {
return d;
});
// jquery sorting applied to it - could be done with d3 and events.
$("table").stupidtable();
});
</script>
</html>
Tool Monthlyviews Monthlyvisits
Archives.gov Blogs 79986 45744
Tumblr Blogs 18079 9585
Facebook 11508850 3474852
Youtube 191964 22860
Flickr 1765243 10798
iTunes U 2260 338
// 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;
cells = rows.selectAll("td")
// each row has data associated; we get it and enter it for the cells.
.data(function(d) {
return d;
})
.enter()
.append("td")
.style("background-color", function(d,i) {
// for the last element in the row, we color the background:
if (i === 3) {
return colorScale(d);
}
})
}
// 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