Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

209 lines (180 sloc) 8.39 KB
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>Sortable dc.js Table Demo - Into the Void</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.3.14/crossfilter.js" type='text/javascript'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.1/dc.min.js" type='text/javascript'></script>
<script src="https://code.jquery.com/jquery-3.1.1.min.js" type='text/javascript'></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" type='text/javascript'></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel='stylesheet' type='text/css'>
<link href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.1/dc.min.css" rel='stylesheet' type='text/css'>
</head>
<body>
<div class="container">
<div class="row">
<div class="table-responsive col-md-12">
<h4>Filtered Records</h4>
<table class='table table-hover table-striped table-bordered' id='dc-table-graph'>
<thead>
<tr class='table-header'>
<!-- Programmatically insert table headers here -->
</tr>
</thead>
</table>
<div class="col-sm-12" id="paging">
Showing <span id="begin"></span>-<span id="end"></span> of <span id="size"></span>
<input id="Prev" class="btn btn-secondary" role="button" type="button" value="Prev" onclick="javascript:prevPage()" />
<input id="Next" class="btn btn-secondary" role="button" type="button" value="Next" onclick="javascript:nextPage()"/>
</div>
</div>
</div>
<div id="vertical-whitespace" style="padding: 10px"></div>
</div>
<style type="text/css">
#paging {
text-align: right;
}
#dc-table-graph th span { /*Glyphicon style*/
float: right;
font-size: 1em;
color: #0080bf;
display: inline-block;
}
#dc-table-graph th {
cursor: pointer;
}
</style>
<script>
var dataTable = dc.dataTable("#dc-table-graph");
var dim = {}, // Stores all crossfilter dimensions
groups = {}, // Stores all crossfilter groups
cf;
d3.json("data.json", function(error, data) {
if (error) throw error;
// Programmatically insert header labels for table
var tableHeader = d3.select(".table-header")
.selectAll("th");
// Bind data to tableHeader selection.
tableHeader = tableHeader.data(
[
{label: "True Label", field_name: "true_label", sort_state: "ascending"},
{label: "Classifier", field_name: "classifier_label", sort_state: "ascending"},
{label: "Misclassified", field_name: "misclassified", sort_state: "ascending"},
{label: "True Conf", field_name: "true_conf", sort_state: "ascending"},
{label: "Max Conf", field_name: "max_conf", sort_state: "descending"} // Note Max Conf row starts off as descending
]
);
// enter() into virtual selection and create new <th> header elements for each table column
tableHeader = tableHeader.enter()
.append("th")
.text(function (d) { return d.label; }) // Accessor function for header titles
.on("click", tableHeaderCallback);
function tableHeaderCallback(d) {
// Highlight column header being sorted and show bootstrap glyphicon
var activeClass = "info";
d3.selectAll("#dc-table-graph th") // Disable all highlighting and icons
.classed(activeClass, false)
.selectAll("span")
.style("visibility", "hidden") // Hide glyphicon
var activeSpan = d3.select(this) // Enable active highlight and icon for active column for sorting
.classed(activeClass, true) // Set bootstrap "info" class on active header for highlight
.select("span")
.style("visibility", "visible");
// Toggle sort order state to user desired state
d.sort_state = d.sort_state === "ascending" ? "descending" : "ascending";
var isAscendingOrder = d.sort_state === "ascending";
dataTable
.order(isAscendingOrder ? d3.ascending : d3.descending)
.sortBy(function(datum) { return datum[d.field_name]; });
// Reset glyph icon for all other headers and update this headers icon
activeSpan.node().className = ''; // Remove all glyphicon classes
// Toggle glyphicon based on ascending/descending sort_state
activeSpan.classed(
isAscendingOrder ? "glyphicon glyphicon-sort-by-attributes" :
"glyphicon glyphicon-sort-by-attributes-alt", true);
updateTable();
dataTable.redraw();
}
// Initialize sort state and sort icon on one of the header columns
// Highlight "Max Conf" cell on page load
// This can be done programmatically for user specified column
tableHeader.filter(function(d) { return d.label === "Max Conf"; })
.classed("info", true);
var tableSpans = tableHeader
.append("span") // For Sort glyphicon on active table headers
.classed("glyphicon glyphicon-sort-by-attributes-alt", true)
.style("visibility", "hidden")
.filter(function(d) { return d.label === "Max Conf"; })
.style("visibility", "visible");
cf = crossfilter(data); // Main crossfilter objects
// Setup different dimensions for plots
dim.tableMaxConfidence = cf.dimension(function (d) {
return d.max_conf;
});
// ##############################
// Generate the dc.js dataTable
// ##############################
// Create generating functions for each columns
var columnFunctions = [
function(d) { return d.true_label; },
function(d) { return d.classifier_label; },
function(d) { return d.misclassified; },
function(d) { return d.true_conf; },
function(d) { return d.max_conf; },
];
// Pagination implementation inspired by:
// https://github.com/dc-js/dc.js/blob/master/web/examples/table-pagination.html
dataTable.width(960).height(800)
.dimension(dim.tableMaxConfidence)
.group(function(d) { return "Dummy"}) // Must pass in. Ignored since .showGroups(false)
.size(Infinity)
.columns(columnFunctions)
.showGroups(false)
.sortBy(function(d){ return d.max_conf; }) // Initially sort by max_conf column
.order(d3.descending);
updateTable();
dataTable.redraw();
}); // End d3.json callback function
// Data Table Pagination
var tableOffset = 0, tablePageSize = 10;
// updateTable calculates correct start and end indices for current page view
// it slices and pulls appropriate date for current page from dataTable object
// Finally, it updates the pagination button states depending on if more records
// are available
function updateTable() {
// Ensure Prev/Next bounds are correct, especially after filters applied to dc charts
var totFilteredRecs = cf.groupAll().value();
// Adjust values of start and end record numbers for edge cases
var end = tableOffset + tablePageSize > totFilteredRecs ? totFilteredRecs : tableOffset + tablePageSize;
tableOffset = tableOffset >= totFilteredRecs ? Math.floor((totFilteredRecs - 1) / tablePageSize) * tablePageSize : tableOffset;
tableOffset = tableOffset < 0 ? 0 : tableOffset; // In case of zero entries
// Grab data for current page from the dataTable object
dataTable.beginSlice(tableOffset);
dataTable.endSlice(tableOffset + tablePageSize);
// Update Table paging buttons and footer text
d3.select('span#begin')
.text(end === 0 ? tableOffset : tableOffset + 1); // Correct for "Showing 1 of 0" bug
d3.select('span#end')
.text(end);
d3.select('#Prev.btn')
.attr('disabled', tableOffset - tablePageSize < 0 ? 'true' : null);
d3.select('#Next.btn')
.attr('disabled', tableOffset + tablePageSize >= totFilteredRecs ? 'true' : null);
d3.select('span#size').text(totFilteredRecs);
dataTable.redraw();
}
// Callback function for clicking "Next" page button
function nextPage() {
tableOffset += tablePageSize;
updateTable();
}
// Callback function for clicking "Prev" page button
function prevPage() {
tableOffset -= tablePageSize;
updateTable();
}
</script>
</body>
</html>
You can’t perform that action at this time.