Skip to content

Instantly share code, notes, and snippets.

@milroc
Last active December 17, 2015 00:00
Show Gist options
  • Save milroc/5518052 to your computer and use it in GitHub Desktop.
Save milroc/5518052 to your computer and use it in GitHub Desktop.
bar + sum: vanilla d3.js

This is a six part series, taking you through stages of designing and creating reusable visualizations with d3.js

All visualizations have the same functionality, showcase the individual points with a bar chart and sum up the selected bars.

Part 1. This is showcasing a prototype visualization made solely with d3.js and vanilla javascript.

These are examples created for a talk (slides and video).

Cheers,

Miles @milr0c

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="http://littlesparkvt.com/flatstrap/assets/css/bootstrap.css"/>
<link type="text/css" rel="stylesheet" href="style.css"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<div class="row">
<div class="span2"><button class="btn btn-success" onclick="update()">update</button></div>
<div class="span2" id="sum">TOTAL: 0</div>
</div>
<div class="row" id="chart"></div>
<script src="src.js"></script>
</body>
</html>
function randomizeData(n, y) {
if (arguments.length < 2) y = 400;
if (!arguments.length) n = 20;
var i = 0;
return d3.range(~~(Math.random()*n) + 1).map(function(d, i) { return {
x: ++i,
y: ~~(Math.random()*y)
}});
}
function update() {
var data = randomizeData(20, Math.random()*100000);
var margin = {top: 0, bottom: 20, left: 0, right: 0},
width = 400,
height = 400,
duration = 500,
formatNumber = d3.format(',d'),
brush = d3.svg.brush();
margin.left = formatNumber(d3.max(data, function(d) { return d.y; })).length * 14;
var w = width - margin.left - margin.right,
h = height - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, w], .1),
y = d3.scale.linear()
.range([h, 0]);
y.domain([0, d3.max(data, function(d) { return d.y; })]);
x.domain(data.map(function(d) { return d.x; }));
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom'),
yAxis = d3.svg.axis()
.scale(y)
.orient('left'),
brush = d3.svg.brush()
.x(x)
.on('brushstart', brushstart)
.on('brush', brushmove)
.on('brushend', brushend);
var svg = d3.select('#chart').selectAll('svg').data([data]),
svgEnter = svg.enter().append('svg')
.append('g')
.attr('width', w)
.attr('height', h)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.classed('chart', true),
chart = d3.select('.chart');
svgEnter.append('g')
.classed('x axis', true)
.attr('transform', 'translate(' + 0 + ',' + h + ')');
svgEnter.append('g')
.classed('y axis', true)
svgEnter.append('g').classed('barGroup', true);
chart.selectAll('.brush').remove();
chart.selectAll('.selected').classed('selected', false);
chart.append('g')
.classed('brush', true)
.call(brush)
.selectAll('rect')
.attr('height', h);
bars = chart.select('.barGroup').selectAll('.bar').data(data);
bars.enter()
.append('rect')
.classed('bar', true)
.attr('x', w) // start here for object constancy
.attr('width', x.rangeBand())
.attr('y', function(d, i) { return y(d.y); })
.attr('height', function(d, i) { return h - y(d.y); });
bars.transition()
.duration(duration)
.attr('width', x.rangeBand())
.attr('x', function(d, i) { return x(d.x); })
.attr('y', function(d, i) { return y(d.y); })
.attr('height', function(d, i) { return h - y(d.y); });
bars.exit()
.transition()
.duration(duration)
.style('opacity', 0)
.remove();
chart.select('.x.axis')
.transition()
.duration(duration)
.call(xAxis);
chart.select('.y.axis')
.transition()
.duration(duration)
.call(yAxis);
function brushstart() {
chart.classed("selecting", true);
}
function brushmove() {
var extent = d3.event.target.extent();
bars.classed("selected", function(d) { return extent[0] <= x(d.x) && x(d.x) + x.rangeBand() <= extent[1]; });
makeSum();
}
function brushend() {
chart.classed("selecting", !d3.event.target.empty());
}
function makeSum() {
var sumDiv = d3.select('#sum'),
extent = brush.extent(),
sum = 0;
data.forEach(function(d) {
if (extent[0] <= x(d.x) && x(d.x) + x.rangeBand() <= extent[1])
sum += d.y;
});
sumDiv.text('TOTAL: ' + sum);
}
makeSum();
}
update();
body {
font: 14px helvetica;
color: #f0f0f0;
background-color: #333;
}
.row {
padding: 5px;
}
.axis path,
.axis line {
fill: none;
stroke: #f0f0f0;
shape-rendering: crispEdges;
}
.axis text {
fill: #f0f0f0;
}
.brush .extent {
stroke: #f0f0f0;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.bar {
fill: #5EB4E3;
}
.selected {
fill: #78C656;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment