Skip to content

Instantly share code, notes, and snippets.

@cirofdo
Last active March 9, 2017 03:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cirofdo/2600596796b1e3094f65db866c7c48a6 to your computer and use it in GitHub Desktop.
Save cirofdo/2600596796b1e3094f65db866c7c48a6 to your computer and use it in GitHub Desktop.
D3 Bar Chart

Bar Chart with ramdom data generation.

Good for understanding Enter, Update and Exit.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: SVG bar chart with value labels (centered)</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="btn-main">
<button class="generateRandom">Generate new data</button>
<button class="addRandom">Add a value</button>
<button class="removeRandom">Remove a value</button>
</div>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
//Width and height
var margin = {top: 20, bottom: 50, right: 150, left: 150};
var w = 960 - margin.right - margin.left;
var h = 350 - margin.top - margin.bottom;
var dataset = [];
// Create 20 random numbers from 1 to 30
for (var i = 0; i < 20; i++) {
var randomNumber = Math.floor(Math.random()*30);
dataset.push(randomNumber);
}
// Scales
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([h, 0]);
// Axes
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
// Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom);
////////////////
// INITIAL ENTER
// Creates rects
svg.selectAll("rect")
.data(dataset)
.enter().append("rect")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// Adjust x position acording to scale
.attr("x", function(d, i) {
return xScale(i);
})
// Adjust y position acording to scale
.attr("y", function(d) {
return yScale(d);
})
// Width and height acordding to scales
.attr("width", xScale.rangeBand()) // need to understand rangeBand() better
.attr("height", function(d) {
return h - yScale(d);
})
// Bars colors proportional to to value
.attr("fill", function(d) {
return "rgb(0, 0, " + (255 - d*8) + ")";
});
// Text that will be labels
svg.selectAll("text")
.data(dataset)
.enter().append("text")
.attr("class", "lbl")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
if (d == 1) {
return yScale(d) + 9;
}
if (d == 0) {
return null;
}
return yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", function(d) { return 0.02*w + "px" })
.attr("font-weight", "bold")
.attr("fill", "white");
// Axes
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + (h + margin.top) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(yAxis);
//////////////////////////////////////////////////////////////////////
// GENERATE RANDOM ARRAY
//
// UPDATE
d3.select(".generateRandom")
.on("click", function() {
// Do something mundane and annoying on click
for (var i = 0; i < dataset.length; i++) {
var randomNumber = Math.floor(Math.random()*50);
dataset[i] = randomNumber;
}
// Update scales domain
xScale.domain(d3.range(dataset.length))
yScale.domain([0, d3.max(dataset)])
/////////// BARS ///////////
//
// Update all rects
svg.selectAll("rect")
.data(dataset)
.transition().duration(500).ease("linear")
.attr("y", function(d) {
return yScale(d);
})
.attr("height", function(d) {
return h - yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (255 - d*8) + ")";
});
/////////// LABELS ///////////
//
// Update text
svg.selectAll("text.lbl")
.data(dataset)
.transition().duration(500).ease("linear")
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
if (d == 1) {
return yScale(d) + 9;
}
if (d == 0) {
return null;
}
return yScale(d) + 14;
});
/////////// AXES ///////////
//
// Update yAxis
svg.selectAll("g.y.axis").call(yAxis);
});
/////////////////////////////////////////////////////////////////////////////
// ADD A NEW RANDOM NUMBER
d3.select(".addRandom")
.on("click", function() {
// Push a new number
dataset.push(Math.floor(Math.random()*50));
// Update scales domain
xScale.domain(d3.range(dataset.length))
yScale.domain([0, d3.max(dataset)])
/////////// BARS ///////////
//
// Select
var bars = svg.selectAll("rect").data(dataset)
// Enter
bars.enter().append("rect").attr("transform", "translate(" + margin.left + "," + margin.top + ")")
// Update
bars.transition().duration(500)
// Ajusta as posicoes para caber na tela de acordo com a escala
.attr("x", function(d, i) {
return xScale(i);
})
// Ajusta a posicao vertical
.attr("y", function(d) {
return yScale(d);
})
// Largura e altura proporcionais ao dataset
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return h - yScale(d);
})
// Cores de acordo c o dataset
.attr("fill", function(d) {
return "rgb(0, 0, " + (255 - d*8) + ")";
});
/////////// LABELS ///////////
//
// Select
var labels = svg.selectAll("text.lbl").data(dataset)
// Enter
labels.enter().append("text")
.attr("class", "lbl")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
//Update
labels.transition().duration(500)
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
if (d == 1) {
return yScale(d) + 9;
}
if (d == 0) {
return null;
}
return yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", function(d) { return 0.02*w + "px" })
.attr("font-weight", "bold")
.attr("fill", "white");
/////////// AXES ///////////
//
// Update
svg.selectAll("g.y.axis").call(yAxis);
if (dataset.length <= 30) {
xAxis.tickValues(xScale.domain().filter(function(d, i) { return !(i % 1); })) // change the ticks back to normal
svg.selectAll("g.x.axis").call(xAxis);
} else {
xAxis.tickValues(xScale.domain().filter(function(d, i) { return !(i % 2); })) // change the ticks if x is large
svg.selectAll("g.x.axis").call(xAxis);
}
});
////////////////////////////////////////////////////////////////////////////
// REMOVES THE LAST RANDOM NUMBER
d3.select(".removeRandom")
.on("click", function() {
// Remove one value from array
dataset.pop();
// Update scales domain
xScale.domain(d3.range(dataset.length))
yScale.domain([0, d3.max(dataset)])
/////////// BARS ///////////
//
// Select
var bars = svg.selectAll("rect").data(dataset);
// Update
bars.transition().duration(500)
.attr("x", function(d, i) {
return xScale(i);
})
// Ajusta a posicao vertical
.attr("y", function(d) {
return yScale(d);
})
// Largura e altura proporcionais ao dataset
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return h - yScale(d);
})
// Cores de acordo c o dataset
.attr("fill", function(d) {
return "rgb(0, 0, " + (255 - d*8) + ")";
});
// Exit
bars.exit() //References the exit selection (a subset of the update selection)
.transition() //Initiates a transition on the one element we're deleting
.duration(500)
.attr("x", w) //Move past the right edge of the SVG
.remove(); //Deletes this element from the DOM once transition is complete
/////////// LABELS ///////////
//
// Select
var labels = d3.selectAll("text.lbl").data(dataset);
// Update
labels.transition().duration(500)
.text(function(d) {
return d;
})
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
if (d == 1) {
return yScale(d) + 9;
}
if (d == 0) {
return null;
}
return yScale(d) + 14;
});
// Exit
labels.exit().remove();
/////////// AXES ///////////
//
// Update
svg.selectAll("g.y.axis").call(yAxis);
if (dataset.length <= 30) {
xAxis.tickValues(xScale.domain().filter(function(d, i) { return !(i % 1); })) // change the ticks back to normal
svg.selectAll("g.x.axis").call(xAxis);
} else {
xAxis.tickValues(xScale.domain().filter(function(d, i) { return !(i % 2); })) // change the ticks if x is large
svg.selectAll("g.x.axis").call(xAxis);
}
});
.btn-main {
text-align: center;
margin: 10px;
}
button {
font-family: Arial;
text-align: center;
position: relative;
margin: 10px;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
rect:hover {
fill: orange;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment