Skip to content

Instantly share code, notes, and snippets.

@djamshed
Last active July 9, 2021 08:51
Show Gist options
  • Save djamshed/1845538 to your computer and use it in GitHub Desktop.
Save djamshed/1845538 to your computer and use it in GitHub Desktop.
Leaderboard in d3.js
<!doctype html>
<head>
<meta charset="utf-8">
<!-- Use the .htaccess and remove these lines to avoid edge case issues.
More info: h5bp.com/b/378 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Leaderboard with D3</title>
<meta name="description" content="">
<meta name="author" content="Djam Saidmuradov">
<!-- Mobile viewport optimized: j.mp/bplateviewport -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href='http://fonts.googleapis.com/css?family=Gudea:400,700' rel='stylesheet'>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<link rel="stylesheet" href="style.css">
<!-- end CSS-->
</head>
<body>
<div id="container">
<header>
</header>
<div id="main" role="main">
<div id="toolbar">
<form class="well form-search">
<input type = "text" title="Search by name" placeholder="search name" id="searchInput"/>
<button type="submit" class="btn" id="searchBtn">Search by name</button>
<span style="margin-left: 20px;">Show:</span>
<a id="desc" class="btn" href="#">Leaders</a>
or
<a id="asc" class ="btn" href="#">Laggards</a>
</form>
</div>
<div id="leaderboard">
</div>
</div>
<footer>
</footer>
</div> <!--! end of #container -->
<!-- JavaScript at the bottom for fast page loading -->
<!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if offline -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src=" https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js"></script>
<script src="https://raw.github.com/cdnjs/cdnjs/master/ajax/libs/twitter-bootstrap/2.0.0/bootstrap.min.js"></script>
<script src="http://d3js.org/d3.v2.min.js"></script>
<!-- scripts concatenated and minified via ant build script-->
<script defer src="plugins.js"></script>
<script defer src="script.js"></script>
<!-- end scripts-->
</body>
</html>
// usage: log('inside coolFunc', this, arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
log.history = log.history || []; // store logs to an array for reference
log.history.push(arguments);
if(this.console) {
arguments.callee = arguments.callee.caller;
var newarr = [].slice.call(arguments);
(typeof console.log === 'object' ? log.apply.call(console.log, console, newarr) : console.log.apply(console, newarr));
}
};
// make it safe to use console.log always
(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,timeStamp,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try
{console.log();return window.console;}catch(err){return window.console={};}})());
// place any jQuery/helper plugins in here, instead of separate, slower script files.
/* Author: Djam
*/
const DATA_SIZE = 15; //num of random data items to generate
const DISPLAYED_ROWS = 5;
const HOVER_DURATION = 1000;
const CELL_WIDTH = 200;
var MODE_ASC = false;
var data = [],
columns,
cells;
//----------------------------------------------- handlers, util functions
function addHandlers (){
$('#searchInput').keypress(function(e){ if((e.keyCode || e.which) == 13 /* Enter */) return searchItems(); });
$('#searchBtn').click(function (e) { return searchItems(); });
$('#asc').click(function (e) {MODE_ASC = true; reloadCells(); return false; });
$('#desc').click(function (e) {MODE_ASC = false; reloadCells(); return false; });
}
function searchItems(){
var searchStr = $('#searchInput').val();
cells.classed('selected', false);
cells.filter(function(cellData){return cellData.name.toLowerCase() === searchStr.toLowerCase();})
.classed('selected', true);
return false;
}
//money format function
var money = d3.format("0,.2f")
//rank format function (returns 1st, 23rd, etc..)
function rank(num)
{
if (num == 0) return "0";
switch (num % 100)
{
case 11:
case 12:
case 13:
return num + "th";
}
switch (num % 10)
{
case 1: return num + "st";
case 2: return num + "nd";
case 3: return num + "rd";
}
return num + "th";
}
//----------------------------------------------- Data
//create data
var columnObj = [
{name: "Total Budget", ascending: false},
{name: "Paid to Date", ascending: false},
{name: "Prior period", ascending: false}
];
function createSampleData(){
for ( var num in d3.range(DATA_SIZE)){
var datum = {name: "Name "+ num};
for( var i in columnObj){
datum[columnObj[i].name] = Math.random() * 100000;
}
data.push(datum);
}
}
//-----------------------------------------------Leaderboard implementation
//removes styles attribute
function removeStyles (el, properties) {
if (properties)
properties.foreach(function (prop) {el.style(prop, null);});
else
el.attr('style', null);
}
//animates selection to hovered state (used for "out of range" cells)
function animateToHovered(selection){
if(selection.empty()) return;
var _height = selection.style('height');
selection.style('height', '0px');
selection.style('opacity', 0.5);
selection
.transition()
.duration(100)
.style('height', _height)
.transition()
.delay(100)
.duration(300)
.style('opacity', 1)
//remove animated properties
.each('end', function (d, i) { removeStyles ( d3.select(this)); })
;
}
//animates selection to selected state (used for "out of range" cells)
function animateToSelected(selection){
if(selection.empty()) return;
var _bgcolor = selection.style('background-color');
var _color = selection.style('color');
//it starts with hovered colors
//FIXME: find a way to get hovered colors
selection.style('background-color', '#DFDFDF');
selection.style('color', '#404040');
//animate to selected colors
selection
.transition()
.duration(500)
.style('background-color', _bgcolor)
.style('color', _color)
//remove animated properties
.each('end', function (d, i) { removeStyles ( d3.select(this)); })
;
}
function initLeaderboard(){
//Columns
columns = d3.select("#leaderboard")
.selectAll(".leaderboard-column")
.data(columnObj)
.text(function(d){ return d.name})
;
// Enter…
columns.enter()
.append("div")
.style("width", function (d, i) { return CELL_WIDTH +"px"; })
.style("left", function (d, i) { return i * CELL_WIDTH +"px"; })
.classed("leaderboard-column", true)
.text(function(d){ return d.name})
;
// Exit…
columns.exit()
.remove();
//Cells
reloadCells();
//Enter
cells.enter()
.append('div')
.classed("leaderboard-cell", true)
.classed('cell-out-of-range', function(d, i){
return i >= DISPLAYED_ROWS;
})
;
//Exit
cells.exit()
.remove();
//cell title
cells.append('div')
.classed("title", true)
.text(function(d,i,j) {
return d['name'];
});
//cell rank+value container
var vals = cells.append('div')
.classed("val-container", true);
//rank
vals.append('div')
.classed("rank", true)
.text(function(d,i,j) {
return rank(i+1);
});
//value
vals.append('div')
.classed("value", true)
.text(function(d,i,j) {
return "$" + money(d[columnObj[j].name]);
});
//------------------------Cell Event handlers
//Cell click
cells.on("click", function(d, i){
//deselect all
cells.classed('selected', false);
//select relevant ones
var selected = cells.filter(function(cellData){ return d === cellData;})
.classed('selected', true)
;
//animate color for those out of range
columns.selectAll('.leaderboard-cell.cell-out-of-range.selected').call(animateToSelected);
});
//cell mouse over
cells.on("mouseover", function(d, i){
var hovered = cells.filter(function(cellData){ return d === cellData;})
.classed('hovered', true)
;
//fade in those out of range
columns.selectAll('.leaderboard-cell.cell-out-of-range.hovered').call(animateToHovered);
});
//cell mouse out
cells.on("mouseout", function(d, i){
var hovered = cells.filter(function(cellData){ return d === cellData;})
.classed('hovered', false)
;
});
}
function reloadCells(){
//Cells
cells =
columns.selectAll(".leaderboard-cell")
.data(
function(columnObj){
//data sorted by the current columnObj
return data.sort(function(a, b){
return MODE_ASC ? d3.ascending(a[columnObj.name], b[columnObj.name])
: d3.descending(a[columnObj.name], b[columnObj.name])
})
}
, function (d){ return d.name; })
.classed('cell-out-of-range', function(d, i){
return i >= DISPLAYED_ROWS;
})
;
cells.order();
}
//-----------------------------------------------Start
function start(){
addHandlers();
createSampleData();
initLeaderboard();
}
start();
/*
* HTML5 ✰ Boilerplate
*
* What follows is the result of much research on cross-browser styling.
* Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
* Kroc Camen, and the H5BP dev community and team.
*
* Detailed information about this CSS: h5bp.com/css
*
* ==|== normalize ==========================================================
*/
/* =============================================================================
HTML5 display definitions
========================================================================== */
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; }
audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; }
audio:not([controls]) { display: none; }
[hidden] { display: none; }
/* =============================================================================
Base
========================================================================== */
/*
* 1. Correct text resizing oddly in IE6/7 when body font-size is set using em units
* 2. Force vertical scrollbar in non-IE
* 3. Prevent iOS text size adjust on device orientation change, without disabling user zoom: h5bp.com/g
*/
html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
body { margin: 0; font-size: 13px; line-height: 1.231; }
body, button, input, select, textarea { font-family: sans-serif; color: #222; }
/*
* Remove text-shadow in selection highlight: h5bp.com/i
* These selection declarations have to be separate
* Also: hot pink! (or customize the background color to match your design)
*/
::-moz-selection { background: #fe57a1; color: #fff; text-shadow: none; }
::selection { background: #fe57a1; color: #fff; text-shadow: none; }
/* =============================================================================
Links
========================================================================== */
a { color: #00e; }
a:visited { color: #551a8b; }
a:hover { color: #06e; }
a:focus { outline: thin dotted; }
/* Improve readability when focused and hovered in all browsers: h5bp.com/h */
a:hover, a:active { outline: 0; }
/* =============================================================================
Typography
========================================================================== */
abbr[title] { border-bottom: 1px dotted; }
b, strong { font-weight: bold; }
blockquote { margin: 1em 40px; }
dfn { font-style: italic; }
hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
ins { background: #ff9; color: #000; text-decoration: none; }
mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; }
/* Redeclare monospace font family: h5bp.com/j */
pre, code, kbd, samp { font-family: monospace, monospace; _font-family: 'courier new', monospace; font-size: 1em; }
/* Improve readability of pre-formatted text in all browsers */
pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; }
q { quotes: none; }
q:before, q:after { content: ""; content: none; }
small { font-size: 85%; }
/* Position subscript and superscript content without affecting line-height: h5bp.com/k */
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sup { top: -0.5em; }
sub { bottom: -0.25em; }
/* =============================================================================
Lists
========================================================================== */
ul, ol { margin: 1em 0; padding: 0 0 0 40px; }
dd { margin: 0 0 0 40px; }
nav ul, nav ol { list-style: none; list-style-image: none; margin: 0; padding: 0; }
/* =============================================================================
Embedded content
========================================================================== */
/*
* 1. Improve image quality when scaled in IE7: h5bp.com/d
* 2. Remove the gap between images and borders on image containers: h5bp.com/e
*/
img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; }
/*
* Correct overflow not hidden in IE9
*/
svg:not(:root) { overflow: hidden; }
/* =============================================================================
Figures
========================================================================== */
figure { margin: 0; }
/* =============================================================================
Forms
========================================================================== */
form { margin: 0; }
fieldset { border: 0; margin: 0; padding: 0; }
/* Indicate that 'label' will shift focus to the associated form element */
label { cursor: pointer; }
/*
* 1. Correct color not inheriting in IE6/7/8/9
* 2. Correct alignment displayed oddly in IE6/7
*/
legend { border: 0; *margin-left: -7px; padding: 0; }
/*
* 1. Correct font-size not inheriting in all browsers
* 2. Remove margins in FF3/4 S5 Chrome
* 3. Define consistent vertical alignment display in all browsers
*/
button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; }
/*
* 1. Define line-height as normal to match FF3/4 (set using !important in the UA stylesheet)
* 2. Correct inner spacing displayed oddly in IE6/7
*/
button, input { line-height: normal; *overflow: visible; }
/*
* Reintroduce inner spacing in 'table' to avoid overlap and whitespace issues in IE6/7
*/
table button, table input { *overflow: auto; }
/*
* 1. Display hand cursor for clickable form elements
* 2. Allow styling of clickable form elements in iOS
*/
button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; }
/*
* Consistent box sizing and appearance
*/
input[type="checkbox"], input[type="radio"] { box-sizing: border-box; }
input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; }
input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
/*
* Remove inner padding and border in FF3/4: h5bp.com/l
*/
button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
/*
* 1. Remove default vertical scrollbar in IE6/7/8/9
* 2. Allow only vertical resizing
*/
textarea { overflow: auto; vertical-align: top; resize: vertical; }
/* Colors for form validity */
input:valid, textarea:valid { }
input:invalid, textarea:invalid { background-color: #f0dddd; }
/* =============================================================================
Tables
========================================================================== */
table { border-collapse: collapse; border-spacing: 0; }
td { vertical-align: top; }
/* ==|== primary styles =====================================================
Author: Djam
========================================================================== */
#main {
margin: 10px;
}
#leaderboard {
font-family: 'Gudea', sans-serif;
font-size: 13px;
color: #404040;
position:relative;
height: 350px;
}
.leaderboard-column {
position:absolute;
text-align: center;
}
.leaderboard-cell {
padding: 5px;
border: 1px solid #D0D2D3;
text-align: left;
cursor: pointer;
background-color: #FFF;
-webkit-transition: all .5s ease;
-moz-transition: all .5s ease;
-o-transition: all .5s ease;
transition: all .5s ease;
}
.leaderboard-cell div {
/*FIXME: disabling mouse events on the cell children, who were firing mouseover/mouseout... use mouseenter/mouseleave instead */
pointer-events: none;
}
.leaderboard-cell .val-container {
position:relative;
height:20px;
}
.leaderboard-cell .rank{
display:none;
position: absolute;
left: 0px;
bottom: 0px;
}
.leaderboard-cell .value{
font-size: 18px;
font-weight: bold;
width:100%;
text-align: right;
position: absolute;
left: 0px;
bottom: 0px;
}
.leaderboard-cell.hovered {
background-color: #DFDFDF;
cursor: pointer;
}
.leaderboard-cell.selected {
background-color: #6AB6DA;
color: white;
}
.leaderboard-cell.cell-out-of-range {
display: none;
-webkit-transition: none;
-moz-transition: none;
-o-transition: all 0s ease;
transition: none;
}
.leaderboard-cell.cell-out-of-range .title {
display:none;
}
.leaderboard-cell.cell-out-of-range .value {
font-size: 13px;
font-weight: normal;
padding-bottom: 2px;
}
.leaderboard-cell.cell-out-of-range.selected, .leaderboard-cell.cell-out-of-range.hovered {
display: block;
}
.leaderboard-cell.hovered .rank, .leaderboard-cell.selected .rank{
display: block;
}
/* ==|== non-semantic helper classes ========================================
Please define your styles before this section.
========================================================================== */
/* For image replacement */
.ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; }
.ir br { display: none; }
/* Hide from both screenreaders and browsers: h5bp.com/u */
.hidden { display: none !important; visibility: hidden; }
/* Hide only visually, but have it available for screenreaders: h5bp.com/v */
.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
/* Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p */
.visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
/* Hide visually and from screenreaders, but maintain layout */
.invisible { visibility: hidden; }
/* Contain floats: h5bp.com/q */
.clearfix:before, .clearfix:after { content: ""; display: table; }
.clearfix:after { clear: both; }
.clearfix { zoom: 1; }
/* ==|== media queries ======================================================
PLACEHOLDER Media Queries for Responsive Design.
These override the primary ('mobile first') styles
Modify as content requires.
========================================================================== */
@media only screen and (min-width: 480px) {
/* Style adjustments for viewports 480px and over go here */
}
@media only screen and (min-width: 768px) {
/* Style adjustments for viewports 768px and over go here */
}
/* ==|== print styles =======================================================
Print styles.
Inlined to avoid required HTTP connection: h5bp.com/r
========================================================================== */
@media print {
* { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } /* Black prints faster: h5bp.com/s */
a, a:visited { text-decoration: underline; }
a[href]:after { content: " (" attr(href) ")"; }
abbr[title]:after { content: " (" attr(title) ")"; }
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */
pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
thead { display: table-header-group; } /* h5bp.com/t */
tr, img { page-break-inside: avoid; }
img { max-width: 100% !important; }
@page { margin: 0.5cm; }
p, h2, h3 { orphans: 3; widows: 3; }
h2, h3 { page-break-after: avoid; }
}
@biovisualize
Copy link

Could you please fix the broken link to d3.js, using http://d3js.org/d3.v2.min.js ? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment