d3.svg.axis
)We aren’t here to chop wood! Axes is the plural of axis, which we’ll say are those little lines that tell you what’s what on a graph.
First, we need to make sure you understand the following not-absolutely-beginner techniques:
d3.select('.bar-graph').selectAll('rect')
instead of d3.selectAll('rect')
.Okay, so let’s say we have our nice chart from before. Wouldn’t it be nice if we knew what the heck those lengths meant?
<svg class="bar"></svg>
<script>
var states = [ {'name' : 'New York', 'total_area': 54555, 'land_area': 47126, 'p_water': 0.136}, {'name' : 'Texas', 'total_area': 268596, 'land_area': 261232, 'p_water': 0.027}, {'name' : 'Arizona', 'total_area': 113990, 'land_area': 113594, 'p_water': 0.003}, {'name' : 'California', 'total_area': 163695, 'land_area': 155779, 'p_water': 0.048}, {'name' : 'Mississippi', 'total_area': 48432, 'land_area': 46923, 'p_water': 0.031}, {'name' : 'New Jersey', 'total_area': 8723, 'land_area': 7354, 'p_water': 0.157} ];
// How wide and tall do we want the svg?
var width = 400;
var height = 150;
// Set the height and width of the svg through d3
var svg = d3.select('.bar').attr('height', height).attr('width', width);
// Create a scale
var bar_scale = d3.scale.linear().domain([0, 300000]).range([0, 400]);
// Draw the rectangles
var rects = svg.selectAll('rect')
.data(states) // bind our data
.enter() // grab the 'new' data points
.append('rect') // append the bars
.style('fill', '#FF0000') // make them red
.attr('x', 0) // set the left hand side to 0
.attr('y', function(d, i) { return i * 15; }) // 15 pixels apart
.attr('height', 10) // each bar is 10 pixels tall
.attr('width', function(d) {
return bar_scale(d['total_area']) // width = total area
});
</script>
The answer is yes, and the answer is axes.
The first thing we need to do is create our axis using d3.svg.axis()
. We chain it with two common methods - .orient
to say which side the numbers should go on, and .scale(bar_scale)
to tell it to listen to our bar scale.
var xAxis = d3.svg.axis()
.orient('bottom')
.scale(bar_scale);
After you create it, you need to append it to your svg. For god knows what reason you do this using a method called .call
:
svg.append('g').call(xAxis);
That g
we’re appending is the div
of the SVG world - just a random container to hold stuff and keep it separate.
To see why we’d do that, use the Web Inspector to try to grab one of the numbers chart below, then poke around in the HTML. You’ll notice there’s a lot of stuff floating around in there.
<svg class="bar"></svg>
<script>
var states = [ {'name' : 'New York', 'total_area': 54555, 'land_area': 47126, 'p_water': 0.136}, {'name' : 'Texas', 'total_area': 268596, 'land_area': 261232, 'p_water': 0.027}, {'name' : 'Arizona', 'total_area': 113990, 'land_area': 113594, 'p_water': 0.003}, {'name' : 'California', 'total_area': 163695, 'land_area': 155779, 'p_water': 0.048}, {'name' : 'Mississippi', 'total_area': 48432, 'land_area': 46923, 'p_water': 0.031}, {'name' : 'New Jersey', 'total_area': 8723, 'land_area': 7354, 'p_water': 0.157} ];
// How wide and tall do we want the svg?
var width = 400;
var height = 150;
// Set the height and width of the svg through d3
var svg = d3.select('.bar').attr('height', height).attr('width', width);
// Create a scale
var bar_scale = d3.scale.linear().domain([0, 300000]).range([0, 400]);
// Draw the rectangles
var rects = svg.selectAll('rect')
.data(states) // bind our data
.enter() // grab the 'new' data points
.append('rect') // append the bars
.style('fill', '#FF0000') // make them red
.attr('x', 0) // set the left hand side to 0
.attr('y', function(d, i) { return i * 15; }) // 15 pixels apart
.attr('height', 10) // each bar is 10 pixels tall
.attr('width', function(d) {
return bar_scale(d['total_area']) // width = total area
});
// CREATING THE AXIS
var xAxis = d3.svg.axis()
.orient('bottom')
.scale(bar_scale);
// ADDING A G ELEMENT, PUTTING THE AXIS IN THE G
svg.append('g').call(xAxis);
</script>
The big problem right now is that our axis is all up in our chart. Somehow we need to move the g
element to be further down the page!
While you’d assume you’d use x
and y
to position it, it sure isn’t the case. This next thing is so important I’m putting it on a line by itself:
When positioning a g
element, you use the transform
attribute. Set it to translate(x, y)
, with x
and y
being the coordinates you’d like to position it at.
svg.append('g')
.attr("transform", "translate(0, 90)");
.call(xAxis)
They sound pretty similar, right? Don’t worry, I always forget which is which. Let’s try it out:
<svg class="bar"></svg>
<script>
var states = [ {'name' : 'New York', 'total_area': 54555, 'land_area': 47126, 'p_water': 0.136}, {'name' : 'Texas', 'total_area': 268596, 'land_area': 261232, 'p_water': 0.027}, {'name' : 'Arizona', 'total_area': 113990, 'land_area': 113594, 'p_water': 0.003}, {'name' : 'California', 'total_area': 163695, 'land_area': 155779, 'p_water': 0.048}, {'name' : 'Mississippi', 'total_area': 48432, 'land_area': 46923, 'p_water': 0.031}, {'name' : 'New Jersey', 'total_area': 8723, 'land_area': 7354, 'p_water': 0.157} ];
// How wide and tall do we want the svg?
var width = 400;
var height = 150;
// Set the height and width of the svg through d3
var svg = d3.select('.bar').attr('height', height).attr('width', width);
// Create a scale
var bar_scale = d3.scale.linear().domain([0, 300000]).range([0, 400]);
// Draw the rectangles
var rects = svg.selectAll('rect')
.data(states) // bind our data
.enter() // grab the 'new' data points
.append('rect') // append the bars
.style('fill', '#FF0000') // make them red
.attr('x', 0) // set the left hand side to 0
.attr('y', function(d, i) { return i * 15; }) // 15 pixels apart
.attr('height', 10) // each bar is 10 pixels tall
.attr('width', function(d) {
return bar_scale(d['total_area']) // width = total area
});
// CREATING THE AXIS
var xAxis = d3.svg.axis()
.orient('bottom')
.scale(bar_scale);
// ADDING A G ELEMENT, PUTTING THE AXIS IN THE G
svg.append('g')
.attr("transform", "translate(0, 90)")
.call(xAxis);
</script>
The thing is, it’s really ugly. It’s really ugly, it’s running off the side of the page, and there are probably more things going on, too.
First, we need to give the g
a class of axis
so we can style it.
svg.append('g')
.attr('class', 'axis')
.attr("transform", "translate(0, 90)");
.call(xAxis)
Then we need to create a <style>
block and add some styling for the axis
class.
<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
Even though the styling looks a little bit better now, we need to push it in from the edges so the numbers don’t overlap so much.
<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<svg class="bar"></svg>
<script>
var states = [ {'name' : 'New York', 'total_area': 54555, 'land_area': 47126, 'p_water': 0.136}, {'name' : 'Texas', 'total_area': 268596, 'land_area': 261232, 'p_water': 0.027}, {'name' : 'Arizona', 'total_area': 113990, 'land_area': 113594, 'p_water': 0.003}, {'name' : 'California', 'total_area': 163695, 'land_area': 155779, 'p_water': 0.048}, {'name' : 'Mississippi', 'total_area': 48432, 'land_area': 46923, 'p_water': 0.031}, {'name' : 'New Jersey', 'total_area': 8723, 'land_area': 7354, 'p_water': 0.157} ];
// How wide and tall do we want the svg?
var width = 400;
var height = 150;
// Set the height and width of the svg through d3
var svg = d3.select('.bar').attr('height', height).attr('width', width);
// Create a scale
var bar_scale = d3.scale.linear().domain([0, 300000]).range([0, 400]);
// Draw the rectangles
var rects = svg.selectAll('rect')
.data(states) // bind our data
.enter() // grab the 'new' data points
.append('rect') // append the bars
.style('fill', '#FF0000') // make them red
.attr('x', 0) // set the left hand side to 0
.attr('y', function(d, i) { return i * 15; }) // 15 pixels apart
.attr('height', 10) // each bar is 10 pixels tall
.attr('width', function(d) {
return bar_scale(d['total_area']) // width = total area
});
// CREATING THE AXIS
var xAxis = d3.svg.axis()
.orient('bottom')
.scale(bar_scale);
// ADDING A G ELEMENT, PUTTING THE AXIS IN THE G
svg.append('g')
.attr('class', 'axis')
.attr("transform", "translate(0, 90)")
.call(xAxis);
</script>
In order to push it in from the edges and give it a little padding, we need to think about it in two steps
We keep it away from the left-hand side by changing the x
coordinates of everything - the rects
can be positioned at x=20
instead of x=0
…
.attr('x', 20) // set the left hand side to 0
And we’ll adjust translate()
with the g
to start 20 pixels in as well.
.attr("transform", "translate(0, 90)")
To keep it away from the right-hand side we’ll need to adjust the length of the bars and the length of the axis. Luckily, it’s all tied into the scale! Let’s just change the .range
on the scale to be 0-360 instead of 0-400.
var bar_scale = d3.scale.linear().domain([0, 300000]).range([0, 360]);
Okay, let’s check in again and see our beautiful, beautiful axis!
<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<svg class="bar"></svg>
<script>
var states = [ {'name' : 'New York', 'total_area': 54555, 'land_area': 47126, 'p_water': 0.136}, {'name' : 'Texas', 'total_area': 268596, 'land_area': 261232, 'p_water': 0.027}, {'name' : 'Arizona', 'total_area': 113990, 'land_area': 113594, 'p_water': 0.003}, {'name' : 'California', 'total_area': 163695, 'land_area': 155779, 'p_water': 0.048}, {'name' : 'Mississippi', 'total_area': 48432, 'land_area': 46923, 'p_water': 0.031}, {'name' : 'New Jersey', 'total_area': 8723, 'land_area': 7354, 'p_water': 0.157} ];
// How wide and tall do we want the svg?
var width = 400;
var height = 150;
// Set the height and width of the svg through d3
var svg = d3.select('.bar').attr('height', height).attr('width', width);
// Create a scale
// don't let it take up all of the svg's space
var bar_scale = d3.scale.linear().domain([0, 300000]).range([0, 360]);
// Draw the rectangles
var rects = svg.selectAll('rect')
.data(states) // bind our data
.enter() // grab the 'new' data points
.append('rect') // append the bars
.style('fill', '#FF0000') // make them red
.attr('x', 20) // push the bars 20 pixels from the left
.attr('y', function(d, i) { return i * 15; }) // 15 pixels apart
.attr('height', 10) // each bar is 10 pixels tall
.attr('width', function(d) {
return bar_scale(d['total_area']) // width = total area
});
// CREATING THE AXIS
var xAxis = d3.svg.axis()
.orient('bottom')
.scale(bar_scale);
// ADDING A G ELEMENT, PUTTING THE AXIS IN THE G
// push it 20 pixels to the left, and 90 pixels down
svg.append('g')
.attr('class', 'axis')
.attr("transform", "translate(20, 90)")
.call(xAxis);
</script>
You can also do a lot more with an axis.
.orient
around a little bit - .orient('top')
, .orient('left')
, .orient('right')
..ticks
. What does .ticks(2)
do?.tickValues
is for when you really want to get picky. Try passing .tickValues(1000, 10000, 100000)
and see what happens! (make sure you do it after .scale
in the chain, though, otherwise it won’t work.)<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
<svg class="bar"></svg>
<script>
var states = [ {'name' : 'New York', 'total_area': 54555, 'land_area': 47126, 'p_water': 0.136}, {'name' : 'Texas', 'total_area': 268596, 'land_area': 261232, 'p_water': 0.027}, {'name' : 'Arizona', 'total_area': 113990, 'land_area': 113594, 'p_water': 0.003}, {'name' : 'California', 'total_area': 163695, 'land_area': 155779, 'p_water': 0.048}, {'name' : 'Mississippi', 'total_area': 48432, 'land_area': 46923, 'p_water': 0.031}, {'name' : 'New Jersey', 'total_area': 8723, 'land_area': 7354, 'p_water': 0.157} ];
// How wide and tall do we want the svg?
var width = 400;
var height = 150;
// Set the height and width of the svg through d3
var svg = d3.select('.bar').attr('height', height).attr('width', width);
// Create a scale
// don't let it take up all of the svg's space
var bar_scale = d3.scale.linear().domain([0, 300000]).range([0, 360]);
// Draw the rectangles
var rects = svg.selectAll('rect')
.data(states) // bind our data
.enter() // grab the 'new' data points
.append('rect') // append the bars
.style('fill', '#FF0000') // make them red
.attr('x', 20) // push the bars 20 pixels from the left
.attr('y', function(d, i) { return i * 15 + 25; }) // 15 pixels apart
.attr('height', 10) // each bar is 10 pixels tall
.attr('width', function(d) {
return bar_scale(d['total_area']) // width = total area
});
// CREATING THE AXIS
var xAxis = d3.svg.axis()
.orient('top')
.scale(bar_scale)
.tickValues([0, 50000, 100000, 200000, 300000]);
// ADDING A G ELEMENT, PUTTING THE AXIS IN THE G
// push it 20 pixels to the left, and 90 pixels down
svg.append('g')
.attr('class', 'axis')
.attr("transform", "translate(20, 20)")
.call(xAxis);
</script>
Cool, right? For more info on axes: