Sortable, Filterable table made with D3.js See the React.js version here
Note: This D3 version of the table doesn't clear the search text onBlur. This allows you to sort the search results.
[ | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-T0H7xR-9iA8/AAAAAAAAAAI/AAAAAAAAAAA/VA9CSBc5Q8A/s88-c-k-no/photo.jpg", | |
"views":"105689500", | |
"created_on":"2011-02-17T13:38:27Z", | |
"title":"psychicpebbles", | |
"id":"UC--BMyA2X4a9PGAo3lTuopg" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-Aqz_Yc964cU/AAAAAAAAAAI/AAAAAAAAAAA/0SkTB3eIHL8/s88-c-k-no/photo.jpg", | |
"views":"229121344", | |
"created_on":"2007-08-18T14:57:17Z", | |
"title":"JustKiddingFilms", | |
"id":"UC-A4oZF4AlOEdlyZWBCI0cQ" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-0b9t8DHdd-8/AAAAAAAAAAI/AAAAAAAAAAA/5xlL-R1Id1M/s88-c-k-no/photo.jpg", | |
"views":"25690295", | |
"created_on":"2006-07-05T08:01:16Z", | |
"title":"Blue Table Painting", | |
"id":"UC-aSLyvFLGEmNFcGomzL47w" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-_9beDwBjMJ4/AAAAAAAAAAI/AAAAAAAAAAA/Jv6rGoAyoz0/s88-c-k-no/photo.jpg", | |
"views":18483830, | |
"created_on":"2012-03-02T15:51:06Z", | |
"title":"dillongoo", | |
"id":"UC-B06UJxJ20HYv15lzrm9mA" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-JqTiqHU5_Hs/AAAAAAAAAAI/AAAAAAAAAAA/oGryI3Ig5U4/s88-c-k-no/photo.jpg", | |
"views":197190215, | |
"created_on":"2008-06-18T06:44:13Z", | |
"title":"MoneySavingVideos", | |
"id":"UC-dch3BP4gKHC4cec0PU1PA" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-o3Zx-Zz8bwA/AAAAAAAAAAI/AAAAAAAAAAA/ZHRS7TqQd8U/s88-c-k-no/photo.jpg", | |
"views":430715, | |
"created_on":"2006-08-03T11:22:11Z", | |
"title":"SPLURT TV", | |
"id":"UC-eeciTxV2kfY6teG3Hu_dQ" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-q03QTYLnRmM/AAAAAAAAAAI/AAAAAAAAAAA/6m7QYo0gRwA/s88-c-k-no/photo.jpg", | |
"views":790458, | |
"created_on":"2009-08-06T12:45:13Z", | |
"title":"Adozie", | |
"id":"UC-jDxvNUzbuFqDzRpAPbFYw" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-HozDGGP43lY/AAAAAAAAAAI/AAAAAAAAAAA/IKNqSHJZ4fw/s88-c-k-no/photo.jpg", | |
"views":16371, | |
"created_on":"2011-09-11T12:04:08Z", | |
"title":"TheNoahHunt", | |
"id":"UC-m0-lV8U6imr5zC-9RMlAA" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-nheWDZWkuaY/AAAAAAAAAAI/AAAAAAAAAAA/UUarZMj10wk/s88-c-k-no/photo.jpg", | |
"views":7561518, | |
"created_on":"2008-01-22T01:00:40Z", | |
"title":"Karliene", | |
"id":"UC-QCyIGEY6DzNyQOnyxIaEg" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-QgxITA1sSog/AAAAAAAAAAI/AAAAAAAAAAA/-fpVz4fdImg/s88-c-k-no/photo.jpg", | |
"views":216964959, | |
"created_on":"2013-08-13T02:42:23Z", | |
"title":"RomanAtwoodVlogs", | |
"id":"UC-SV8-bUJfXjrRMnp7F8Wzw" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-qaWKaxiiuDc/AAAAAAAAAAI/AAAAAAAAAAA/0EoZzkJEUMc/s88-c-k-no/photo.jpg", | |
"views":460971, | |
"created_on":"2011-05-09T03:43:36Z", | |
"title":"Donovan Dustin", | |
"id":"UC-uzRXWfXKZWHDvTCAQRfWA" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-xSoc6icoyM0/AAAAAAAAAAI/AAAAAAAAAAA/WzMp9TXtYek/s88-c-k-no/photo.jpg", | |
"views":55921, | |
"created_on":"2013-11-27T03:22:11Z", | |
"title":"Tab's Gaming Pad", | |
"id":"UC-vkS8J9ovJFA7krXkbA90w" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-qrD04Q_Asr0/AAAAAAAAAAI/AAAAAAAAAAA/IQmGgs75q7o/s88-c-k-no/photo.jpg", | |
"views":290726, | |
"created_on":"2011-10-12T03:13:19Z", | |
"title":"planethumanofficial", | |
"id":"UC-WMky94nwO2V0j3yWzJJ5g" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-X_LNAKWmetg/AAAAAAAAAAI/AAAAAAAAAAA/dPPK1tgp6H8/s88-c-k-no/photo.jpg", | |
"views":1786129, | |
"created_on":"2010-01-24T19:43:24Z", | |
"title":"vedrim", | |
"id":"UC-zKPn4jKeg_sEYjoOFe13w" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-Xjk-VC5VItM/AAAAAAAAAAI/AAAAAAAAAAA/rrBWC34Z7iA/s88-c-k-no/photo.jpg", | |
"views":7086046, | |
"created_on":"2008-06-19T03:52:00Z", | |
"title":"ABCD123toast", | |
"id":"UC04ytXliFP2zZpyFiZYvLQA" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-i65yDNbvKjk/AAAAAAAAAAI/AAAAAAAAAAA/typlIFS918Q/s88-c-k-no/photo.jpg", | |
"views":23180487, | |
"created_on":"2009-09-12T11:04:24Z", | |
"title":"MattKeck", | |
"id":"UC04ZW2FS8RfUmJEa39_-JoA" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-aXujGP8Dyzs/AAAAAAAAAAI/AAAAAAAAAAA/dSDxODBTGfI/s88-c-k-no/photo.jpg", | |
"views":666872, | |
"created_on":"2012-02-28T00:49:50Z", | |
"title":"Jesse Royal", | |
"id":"UC0CohPJt1XAP0sAKazrsxIQ" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-dQFcuLsbK20/AAAAAAAAAAI/AAAAAAAAAAA/r9hmz7DWVSg/s88-c-k-no/photo.jpg", | |
"views":294623, | |
"created_on":"2011-05-03T05:07:35Z", | |
"title":"Corrie Hegwood", | |
"id":"UC0cuBvfn-k0mFkPI9ZeF5vA" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-A_J7gPmZf-Q/AAAAAAAAAAI/AAAAAAAAAAA/n8yz1SmTU_k/s88-c-k-no/photo.jpg", | |
"views":643218, | |
"created_on":"2011-02-04T03:04:04Z", | |
"title":"laughspin", | |
"id":"UC0D3tWXruCZrBZqw0ojzqJQ" | |
}, | |
{ | |
"thumb_url_default":"https://yt3.ggpht.com/-MdLmmFh3qjo/AAAAAAAAAAI/AAAAAAAAAAA/lcGE1l_gam4/s88-c-k-no/photo.jpg", | |
"views":3106606, | |
"created_on":"2009-06-11T04:06:53Z", | |
"title":"Felicia Ricci", | |
"id":"UC0KJrVR7lOqDTkH2S2tjo5Q" | |
} | |
] |
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
background-color: #d1e5f0; | |
font: 14px sans-serif; | |
} | |
#container { | |
width: 100%; | |
height: 100%; | |
position: relative; | |
} | |
#title { | |
font: 26px sans-serif; | |
position: absolute; | |
top: -40px; | |
left: 450px; | |
} | |
#FilterableTable { | |
width: 100%; | |
height: 100%; | |
position: absolute; | |
top: 40px; | |
left: 20px; | |
} | |
.SearchBar { | |
display: inline; | |
position: relative; | |
left: 1%; | |
} | |
.SearchBar input { | |
position: relative; | |
left: 2%; | |
} | |
table { | |
position: absolute; | |
top: 40px; | |
left: 20px; | |
border-collapse: collapse; | |
margin-bottom: 20px; | |
} | |
table a:link, a:visited { text-decoration: none; } | |
table a:hover, a:active { text-decoration: underline; } | |
table, th, td { border: 1px solid black; } | |
td, th { | |
padding: 5px; | |
text-align: center; | |
height: 20px; | |
} | |
th { | |
background-color: #4393c3; | |
color: #d9f0a3; | |
} | |
td { background-color: #92c5de; } | |
tr:hover td { background-color: #edf8b1; } | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.js"></script> | |
<script> | |
var column_names = ["Title","Views","Created On","URL"]; | |
var clicks = {title: 0, views: 0, created_on: 0, url: 0}; | |
// draw the table | |
d3.select("body").append("div") | |
.attr("id", "container") | |
d3.select("#container").append("div") | |
.attr("id", "FilterableTable"); | |
d3.select("#FilterableTable").append("h1") | |
.attr("id", "title") | |
.text("My Youtube Channels") | |
d3.select("#FilterableTable").append("div") | |
.attr("class", "SearchBar") | |
.append("p") | |
.attr("class", "SearchBar") | |
.text("Search By Title:"); | |
d3.select(".SearchBar") | |
.append("input") | |
.attr("class", "SearchBar") | |
.attr("id", "search") | |
.attr("type", "text") | |
.attr("placeholder", "Search..."); | |
var table = d3.select("#FilterableTable").append("table"); | |
table.append("thead").append("tr"); | |
var headers = table.select("tr").selectAll("th") | |
.data(column_names) | |
.enter() | |
.append("th") | |
.text(function(d) { return d; }); | |
var rows, row_entries, row_entries_no_anchor, row_entries_with_anchor; | |
d3.json("data.json", function(data) { // loading data from server | |
// draw table body with rows | |
table.append("tbody") | |
// data bind | |
rows = table.select("tbody").selectAll("tr") | |
.data(data, function(d){ return d.id; }); | |
// enter the rows | |
rows.enter() | |
.append("tr") | |
// enter td's in each row | |
row_entries = rows.selectAll("td") | |
.data(function(d) { | |
var arr = []; | |
for (var k in d) { | |
if (d.hasOwnProperty(k)) { | |
arr.push(d[k]); | |
} | |
} | |
return [arr[3],arr[1],arr[2],arr[0]]; | |
}) | |
.enter() | |
.append("td") | |
// draw row entries with no anchor | |
row_entries_no_anchor = row_entries.filter(function(d) { | |
return (/https?:\/\//.test(d) == false) | |
}) | |
row_entries_no_anchor.text(function(d) { return d; }) | |
// draw row entries with anchor | |
row_entries_with_anchor = row_entries.filter(function(d) { | |
return (/https?:\/\//.test(d) == true) | |
}) | |
row_entries_with_anchor | |
.append("a") | |
.attr("href", function(d) { return d; }) | |
.attr("target", "_blank") | |
.text(function(d) { return d; }) | |
/** search functionality **/ | |
d3.select("#search") | |
.on("keyup", function() { // filter according to key pressed | |
var searched_data = data, | |
text = this.value.trim(); | |
var searchResults = searched_data.map(function(r) { | |
var regex = new RegExp("^" + text + ".*", "i"); | |
if (regex.test(r.title)) { // if there are any results | |
return regex.exec(r.title)[0]; // return them to searchResults | |
} | |
}) | |
// filter blank entries from searchResults | |
searchResults = searchResults.filter(function(r){ | |
return r != undefined; | |
}) | |
// filter dataset with searchResults | |
searched_data = searchResults.map(function(r) { | |
return data.filter(function(p) { | |
return p.title.indexOf(r) != -1; | |
}) | |
}) | |
// flatten array | |
searched_data = [].concat.apply([], searched_data) | |
// data bind with new data | |
rows = table.select("tbody").selectAll("tr") | |
.data(searched_data, function(d){ return d.id; }) | |
// enter the rows | |
rows.enter() | |
.append("tr"); | |
// enter td's in each row | |
row_entries = rows.selectAll("td") | |
.data(function(d) { | |
var arr = []; | |
for (var k in d) { | |
if (d.hasOwnProperty(k)) { | |
arr.push(d[k]); | |
} | |
} | |
return [arr[3],arr[1],arr[2],arr[0]]; | |
}) | |
.enter() | |
.append("td") | |
// draw row entries with no anchor | |
row_entries_no_anchor = row_entries.filter(function(d) { | |
return (/https?:\/\//.test(d) == false) | |
}) | |
row_entries_no_anchor.text(function(d) { return d; }) | |
// draw row entries with anchor | |
row_entries_with_anchor = row_entries.filter(function(d) { | |
return (/https?:\/\//.test(d) == true) | |
}) | |
row_entries_with_anchor | |
.append("a") | |
.attr("href", function(d) { return d; }) | |
.attr("target", "_blank") | |
.text(function(d) { return d; }) | |
// exit | |
rows.exit().remove(); | |
}) | |
/** sort functionality **/ | |
headers | |
.on("click", function(d) { | |
if (d == "Title") { | |
clicks.title++; | |
// even number of clicks | |
if (clicks.title % 2 == 0) { | |
// sort ascending: alphabetically | |
rows.sort(function(a,b) { | |
if (a.title.toUpperCase() < b.title.toUpperCase()) { | |
return -1; | |
} else if (a.title.toUpperCase() > b.title.toUpperCase()) { | |
return 1; | |
} else { | |
return 0; | |
} | |
}); | |
// odd number of clicks | |
} else if (clicks.title % 2 != 0) { | |
// sort descending: alphabetically | |
rows.sort(function(a,b) { | |
if (a.title.toUpperCase() < b.title.toUpperCase()) { | |
return 1; | |
} else if (a.title.toUpperCase() > b.title.toUpperCase()) { | |
return -1; | |
} else { | |
return 0; | |
} | |
}); | |
} | |
} | |
if (d == "Views") { | |
clicks.views++; | |
// even number of clicks | |
if (clicks.views % 2 == 0) { | |
// sort ascending: numerically | |
rows.sort(function(a,b) { | |
if (+a.views < +b.views) { | |
return -1; | |
} else if (+a.views > +b.views) { | |
return 1; | |
} else { | |
return 0; | |
} | |
}); | |
// odd number of clicks | |
} else if (clicks.views % 2 != 0) { | |
// sort descending: numerically | |
rows.sort(function(a,b) { | |
if (+a.views < +b.views) { | |
return 1; | |
} else if (+a.views > +b.views) { | |
return -1; | |
} else { | |
return 0; | |
} | |
}); | |
} | |
} | |
if (d == "Created On") { | |
clicks.created_on++; | |
if (clicks.created_on % 2 == 0) { | |
// sort ascending: by date | |
rows.sort(function(a,b) { | |
// grep date and time, split them apart, make Date objects for comparing | |
var date = /[\d]{4}-[\d]{2}-[\d]{2}/.exec(a.created_on); | |
date = date[0].split("-"); | |
var time = /[\d]{2}:[\d]{2}:[\d]{2}/.exec(a.created_on); | |
time = time[0].split(":"); | |
var a_date_obj = new Date(+date[0],(+date[1]-1),+date[2],+time[0],+time[1],+time[2]); | |
date = /[\d]{4}-[\d]{2}-[\d]{2}/.exec(b.created_on); | |
date = date[0].split("-"); | |
time = /[\d]{2}:[\d]{2}:[\d]{2}/.exec(b.created_on); | |
time = time[0].split(":"); | |
var b_date_obj = new Date(+date[0],(+date[1]-1),+date[2],+time[0],+time[1],+time[2]); | |
if (a_date_obj < b_date_obj) { | |
return -1; | |
} else if (a_date_obj > b_date_obj) { | |
return 1; | |
} else { | |
return 0; | |
} | |
}); | |
// odd number of clicks | |
} else if (clicks.created_on % 2 != 0) { | |
// sort descending: by date | |
rows.sort(function(a,b) { | |
// grep date and time, split them apart, make Date objects for comparing | |
var date = /[\d]{4}-[\d]{2}-[\d]{2}/.exec(a.created_on); | |
date = date[0].split("-"); | |
var time = /[\d]{2}:[\d]{2}:[\d]{2}/.exec(a.created_on); | |
time = time[0].split(":"); | |
var a_date_obj = new Date(+date[0],(+date[1]-1),+date[2],+time[0],+time[1],+time[2]); | |
date = /[\d]{4}-[\d]{2}-[\d]{2}/.exec(b.created_on); | |
date = date[0].split("-"); | |
time = /[\d]{2}:[\d]{2}:[\d]{2}/.exec(b.created_on); | |
time = time[0].split(":"); | |
var b_date_obj = new Date(+date[0],(+date[1]-1),+date[2],+time[0],+time[1],+time[2]); | |
if (a_date_obj < b_date_obj) { | |
return 1; | |
} else if (a_date_obj > b_date_obj) { | |
return -1; | |
} else { | |
return 0; | |
} | |
}); | |
} | |
} | |
if (d == "URL") { | |
clicks.url++; | |
// even number of clicks | |
if (clicks.url % 2 == 0) { | |
// sort ascending: alphabetically | |
rows.sort(function(a,b) { | |
if (a.thumb_url_default.toUpperCase() < b.thumb_url_default.toUpperCase()) { | |
return -1; | |
} else if (a.thumb_url_default.toUpperCase() > b.thumb_url_default.toUpperCase()) { | |
return 1; | |
} else { | |
return 0; | |
} | |
}); | |
// odd number of clicks | |
} else if (clicks.url % 2 != 0) { | |
// sort descending: alphabetically | |
rows.sort(function(a,b) { | |
if (a.thumb_url_default.toUpperCase() < b.thumb_url_default.toUpperCase()) { | |
return 1; | |
} else if (a.thumb_url_default.toUpperCase() > b.thumb_url_default.toUpperCase()) { | |
return -1; | |
} else { | |
return 0; | |
} | |
}); | |
} | |
} | |
}) // end of click listeners | |
}); | |
d3.select(self.frameElement).style("height", "780px").style("width", "1150px"); | |
</script> |