Skip to content

Instantly share code, notes, and snippets.

@parnelandr
Last active January 1, 2016 06:59
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 parnelandr/8108257 to your computer and use it in GitHub Desktop.
Save parnelandr/8108257 to your computer and use it in GitHub Desktop.
Reusable Horizontal Bar Chart

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

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

Need to fix text function for xaxis labels.

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

Food by Deliciousness
Source: Andrew Parnell
Metadata Notes: Things I think are yummy
Food Rating
Cookies 8
Cabbage 2
Pizza 9
Ice Cream 10
Carrots 3.5
Peanut Butter 7
Honey 5
Tomatoes 3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Reusable Horizontal 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,
leftMargin = canvasWidth * 0.25,
maxBarWidth = canvasHeight - - otherMargins - leftMargin
maxChartHeight = canvasHeight - (otherMargins * 2);
//set up linear scale for data to fit on chart area
var xScale = d3.scale.linear()
.range([0, maxBarWidth]);
//set up ordinal scale for x variables
var yScale = d3.scale.ordinal();
//add canvas to HTML
var chart = d3.select("body").append("svg")
.attr("width",canvasWidth)
.attr("height", canvasHeight);
//set up x axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(5);
//set up y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.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 namesTitle = keys[0];
var valuesTitle = keys[1];
//accessing the properties of each object with the variable name through its key
var values = function(d) {return +d[valuesTitle];};
var names = function(d) {return d[namesTitle];}
// find highest value
var maxValue = d3.max(data, values);
//set y domain by mapping an array of the variables along x axis
yScale.domain(data.map(names));
//set x domain with max value and use .nice() to ensure the y axis is labelled above the max y value
xScale.domain([0, maxValue]).nice();
//set bar width with rangeBands ([x axis width], gap between bars, gap before and after bars) as a proportion of bar width
yScale.rangeBands([0, maxChartHeight], 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", leftMargin)
.attr("y", function (d) {return yScale(d[namesTitle]) + otherMargins; })
.attr("width", function (d) {return xScale(d[valuesTitle])})
.attr("height", yScale.rangeBand())
.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("width")) + leftMargin + 6);
var yPosition = parseFloat(d3.select(this).attr("y")) + (parseFloat(d3.select(this).attr("height")) / 2);
chart.append("text")
.attr("id", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition)
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(d[valuesTitle]);
})
//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", canvasWidth / 2)
.attr("y", otherMargins / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(title);
//append x axis
chart.append("g")
.attr("transform", "translate(" + leftMargin + ", " + (maxChartHeight + 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(xAxis)
.selectAll("text")
.attr("stroke", "none")
.attr("fill", "black");
//append y axis
chart.append("g")
.attr("transform", "translate(" + leftMargin + ", " + 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("dx", "-1.15em")
.attr("stroke", "none")
.attr("fill", "black")
//.call(yScale.rangeBand()); //calls wrap function below
//x axis title
chart.append("text")
.attr("x", (maxBarWidth / 2) + leftMargin)
.attr("y", canvasHeight - (otherMargins / 3))
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(valuesTitle);
//y axis title
chart.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -((maxChartHeight / 2) + otherMargins))
.attr("y", leftMargin / 4)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(namesTitle);
//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