Skip to content

Instantly share code, notes, and snippets.

@parnelandr
Last active December 30, 2015 21:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save parnelandr/7887491 to your computer and use it in GitHub Desktop.
Save parnelandr/7887491 to your computer and use it in GitHub Desktop.
Reusable Bar Chart

Simple bar chart that takes in a .csv. Particular format for sharing data. Bar chart handles changes in values and changes in the amount of records.

No CSS. Only need access to the header to reference d3.

Can be found here: https://gist.github.com/parnelandr/7887491

<script src="http://d3js.org/d3.v3.min.js"></script>

24/12/2013 - CSS removed as had a mix of where styling was in the code. Mainly for flexibility with webpages run in a CMS. Adjustments made to bottom margin and x-axis title placement allowing more chart space.

Food by Deliciousness
Source: Andrew Parnell
Metadata Notes: Things I think are yummy
Food Rating
Bananas 5
Green Beans 4
Egg Salad Sandwich 7
Ice Cream 10
Vegemite 3.5
Apples 7
Peaches 6
Figs 2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Reusable Bar Chart</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script type="text/javascript">
//set up canvas and bar sizes
var canvasWidth = 600,
canvasHeight = 500,
otherMargins = canvasWidth * 0.1,
bottomMargin = canvasHeight * 0.2,
maxBarHeight = canvasHeight - otherMargins - bottomMargin,
maxChartWidth = canvasWidth - otherMargins * 2;
//set up linear scale for data to fit on chart area
var yScale = d3.scale.linear()
.range([maxBarHeight, 0]);
//set up ordinal scale for x variables
var xScale = d3.scale.ordinal();
//add canvas to HTML
var chart = d3.select("body").append("svg")
.attr("width",canvasWidth)
.attr("height", canvasHeight);
//set up y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
//set up x axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickSize(0);
//add in data
//csv has 4 lines before the data to be graphed
//First line - Title of the chart
//Second line - Source of the data
//Third line - Metadata notes
//Fourth line - blank
//Fifth line - x variable name, y variable name
//Sixth line onwards - x variable value, y variable value
d3.xhr("Food.csv").get(function (error, response) {
//retrieve data
var dirtyCSV = response.responseText;
var cleanCSV = dirtyCSV.split('\n').slice(4).join('\n');
var data = d3.csv.parse(cleanCSV)
//retrieve title
var dirtyTitle = dirtyCSV.split('\n').slice(0,1).join('\n');
var title = dirtyTitle.slice(0,-1);
//get variable names
var keys = d3.keys(data[0]);
var xName = keys[0];
var yName = keys[1];
//accessing the properties of each object with the variable name through its key
var yData = function(d) {return +d[yName];};
var xData = function(d) {return d[xName];}
// find highest value
var maxY = d3.max(data, yData);
//set x domain by mapping an array of the variables along x axis
xScale.domain(data.map(xData));
//set y domain with max y value and use .nice() to ensure the y axis is labelled above the max y value
yScale.domain([0, maxY]).nice();
//set bar width with rangeBands ([x axis width], gap between bars, gap before and after bars) as a proportion of bar width
xScale.rangeBands([0, maxChartWidth], 0.1, 0.25);
//set up rectangle elements with attributes based on data
var rects = chart.selectAll("rect")
.data(data)
.enter()
.append("rect");
//set up attributes of svg rectangle elements based on attributes
var rectAttributes = rects
.attr("x", function (d) {return (xScale(d[xName])) + otherMargins; })
.attr("y", function (d) {return yScale(d[yName]) + otherMargins; })
.attr("width", xScale.rangeBand())
.attr("height", function (d) {return maxBarHeight - yScale(d[yName])})
.attr("fill", "blue")
.on("mouseover", function(d, i) {
//change fill
d3.select(this)
.attr("fill", "orange");
//set up data to show on mouseover
var xPosition = parseFloat(d3.select(this).attr("x")) + (parseFloat(d3.select(this).attr("width")) / 2);
var yPosition = (parseFloat(d3.select(this).attr("y")) - 6);
chart.append("text")
.attr("id", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(d[yName]);
})
//transition out
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(250)
.attr("fill", "blue");
d3.select("#tooltip").remove();
});
//chart title
chart.append("text")
.attr("x", (maxChartWidth / 2) + otherMargins)
.attr("y", otherMargins / 2)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(title);
//append y axis
chart.append("g")
.attr("transform", "translate(" + otherMargins + ", " + otherMargins + ")")
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.style("stroke", "black")
.style("fill", "none")
.style("stroke-width", 1)
.style("shape-rendering", "crispEdges")
.call(yAxis)
.selectAll("text")
.attr("stroke", "none")
.attr("fill", "black");
//y axis title
chart.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -((maxBarHeight / 2) + otherMargins))
.attr("y", otherMargins / 2)
.attr("dy", "-0.5em")
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(yName);
//append x axis
chart.append("g")
.attr("transform", "translate(" + otherMargins + ", " + (canvasHeight - bottomMargin) + ")")
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.style("stroke", "black")
.style("fill", "none")
.style("stroke-width", 1)
.style("shape-rendering", "crispEdges")
.call(xAxis)
.selectAll("text")
.attr("dy", "1.15em")
.attr("stroke", "none")
.attr("fill", "black")
.call(wrap, xScale.rangeBand()); //calls wrap function below
//x axis title
chart.append("text")
.attr("x", (maxChartWidth / 2) + otherMargins)
.attr("y", canvasHeight - (bottomMargin / 3))
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(xName);
//chart border - not necessary used for reference for the edge of canvas
var border = chart.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", canvasHeight)
.attr("width", canvasWidth)
.style("stroke", "black")
.style("fill", "none")
.style("stroke-width", 1);
//log anything in the console for debugging
//console.log(yAxis);
});
//line wrap function adapted from "Wrapping Long Labels" - mike bostock
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, //em
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
})
}
//while the data is being loaded it turns the strings into a number
function type(d) {
d[yName] = +d[yName];
return d;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment