Skip to content

Instantly share code, notes, and snippets.

@eagereyes
Last active June 7, 2016 21:09
Show Gist options
  • Save eagereyes/1a3ac476c7d21e0f7b00 to your computer and use it in GitHub Desktop.
Save eagereyes/1a3ac476c7d21e0f7b00 to your computer and use it in GitHub Desktop.
Multiples chart in D3 to show how much more CEOs make than average workers.
<!DOCTYPE html>
<html>
<head>
<title>Large Multiples</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style>
rect {
fill: #ddd;
stroke: none;
}
rect.active, circle.unit, circle.worker {
fill: steelblue;
}
text {
font-family: Helvetica, Arial, sans-serif;
fill: lightgray;
}
text.barlabel {
fill: white;
pointer-events: none;
}
text.label, #sentence, .activestep {
fill: darkgray;
}
.step:hover {
fill: #ccc;
}
h1 {
font-family: Helvetica, Arial, sans-serif;
margin-bottom: 0px;
margin-left: 5px;
}
</style>
</head>
<body>
<h1>It pays to be a CEO in the U.S.</h1>
<div id="chart">
</div>
<script>
var data = [
{country: "Poland", multiple: 28, article: ""},
{country: "Austria", multiple: 36, article: ""},
{country: "Denmark", multiple: 48, article: ""},
{country: "Portugal", multiple: 53, article: ""},
{country: "Norway", multiple: 58, article: ""},
{country: "Japan", multiple: 67, article: ""},
{country: "Israel", multiple: 76, article: ""},
{country: "United Kingdom", multiple: 84, article: "the "},
{country: "Sweden", multiple: 89, article: ""},
{country: "Australia", multiple: 93, article: ""},
{country: "France", multiple: 104, article: ""},
{country: "Czech Republic", multiple: 110, article: "the "},
{country: "Spain", multiple: 127, article: ""},
{country: "Germany", multiple: 147, article: ""},
{country: "Switzerland", multiple: 148, article: ""},
{country: "United States", multiple: 354, article: "the "}
];
var intervalID = -1;
var TIMEOUT = 20;
function makeSquares(numUnits) {
svg.selectAll('.unit').remove();
var a = Math.floor(Math.sqrt(numUnits)) // numUnits/.75 for 4:3 ratio of units
var b = Math.floor(numUnits/a);
var rest = numUnits-a*b;
if (rest > a) {
a += 1;
rest = numUnits-a*b;
}
if (b > a) {
var temp = a;
a = b;
b = temp;
}
var circles = [];
for (var y = 0; y < b; y += 1) {
for (var x = 0; x < a; x += 1) {
var c = svg.append('circle')
.attr('cx', unitsOffset+x*(unitsize+unitpadding))
.attr('cy', unitsHeight-y*(unitsize+unitpadding))
.attr('r', unitsize/2)
.style('visibility', 'hidden')
.attr('class', 'unit');
circles.push(c);
}
}
for (var x = 0; x < rest; x += 1) {
var c = svg.append('circle')
.attr('cx', unitsOffset+x*(unitsize+unitpadding))
.attr('cy', unitsHeight-b*(unitsize+unitpadding))
.attr('r', unitsize/2)
.style('visibility', 'hidden')
.attr('class', 'unit');
circles.push(c);
}
svg.select('#ceolabel').transition().attr('y', unitsHeight-b*(unitsize+unitpadding)-16);
if (intervalID != -1) {
window.clearInterval(intervalID);
}
var circleIndex = 0;
intervalID = window.setInterval(function() {
circles[circleIndex].style('visibility', 'visible');
circleIndex += 1;
if (circleIndex == circles.length) {
window.clearInterval(intervalID);
intervalID = -1;
}
}, TIMEOUT);
}
var svg = d3.select('#chart').append('svg').attr('width', 694).attr('height', 470);
var barheight = 24;
var barpad = 3;
var unitsize = 17;
var unitpadding = 3;
var topOffset = 30;
var unitsOffset = 320;
var unitsHeight = topOffset+16*barheight+15*barpad-unitsize/2;
var selected = data[0];
function select(d) {
svg.selectAll('.'+selected.country.replace(' ', '-')).classed('active', false);
svg.select('.barlabel.'+selected.country.replace(' ', '-')).text(selected.country);
svg.selectAll('.'+d.country.replace(' ', '-')).classed('active', true);
makeSquares(d.multiple);
svg.select('.barlabel.'+d.country.replace(' ', '-')).text(d.country+': '+d.multiple+'x');
svg.select('#sentence').text('In '+d.article+d.country+', the average CEO makes '+d.multiple+' times the salary of the average worker.')
selected = d;
svg.select('.activestep').classed('activestep', false);
activeStep = -1;
for (var i = 0; i < steps.length; i++) {
if (data[steps[i]] == d) {
svg.select('.step-'+i).classed('activestep', true);
activeStep = i;
}
}
}
svg.selectAll('.bar').data(data).enter()
.append('rect')
.attr('width', 170)
.attr('height', barheight)
.attr('x', 5)
.attr('y', function(d, i) { return topOffset+i*(barheight+barpad); })
.attr('class', function(d) { return 'bar '+d.country.replace(' ', '-'); })
.on('mouseenter', function(d) {
select(d);
});
svg.selectAll('.barlabel').data(data).enter()
.append('text')
.attr('x', barpad*2)
.attr('y', function(d, i) { return topOffset+i*(barheight+barpad)+barheight-barpad-4; })
.attr('class', function(d) { return 'barlabel '+d.country.replace(' ', '-'); })
.text(function(d) { return d.country; });
svg.append('circle')
.attr('class', 'worker')
.attr('cx', unitsOffset-4*(unitsize+unitpadding))
.attr('cy', unitsHeight)
.attr('r', unitsize/2);
svg.append('text')
.attr('class', 'label')
.attr('x', unitsOffset-4*(unitsize+unitpadding)-unitsize/2)
.attr('y', unitsHeight-16)
.text('Worker');
svg.append('text')
.attr('class', 'label')
.attr('x', unitsOffset-unitsize/2)
.attr('y', unitsHeight-16)
.attr('id', 'ceolabel')
.text('CEO');
svg.append('text')
.attr('id', 'sentence')
.attr('x', 5)
.attr('y', 18);
var steps = [0, 3, 7, 13, 15];
var activeStep = 0;
var stepsLeft = 493;
svg.selectAll('.step').data(steps).enter()
.append('rect')
.attr('class', function(d, i) { return 'step step-'+i+(i==0?' activestep':''); })
.attr('x', function(d, i) { return stepsLeft+i*(barheight+barpad); })
.attr('y', topOffset)
.attr('width', barheight)
.attr('height', barheight)
.on('click', function(d, i) {
svg.select('.activestep').classed('activestep', false);
svg.select('.step-'+i).classed('activestep', true);
activeStep = i;
select(data[d]);
});
svg.selectAll('.steplabel').data(steps).enter()
.append('text')
.attr('class', 'barlabel')
.attr('x', function(d, i) { return stepsLeft+7+i*(barheight+barpad); })
.attr('y', topOffset+17)
.text(function(d, i) { return ''+(i+1); });
svg.append('rect')
.attr('class', 'step')
.attr('x', stepsLeft+steps.length*(barheight+barpad))
.attr('y', topOffset)
.attr('width', barheight*2.5)
.attr('height', barheight)
.on('click', function() {
if (activeStep < steps.length-1) {
svg.select('.activestep').classed('activestep', false);
activeStep += 1;
svg.select('.step-'+activeStep).classed('activestep', true);
select(data[steps[activeStep]]);
}
});
svg.append('text')
.attr('class', 'barlabel')
.attr('x', stepsLeft+7+steps.length*(barheight+barpad))
.attr('y', topOffset+17)
.text('Next >');
select(data[0]);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment