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> |