The SVG viewBox
attribute is a great way to make D3 charts responsive to window resizing. Let's look at an example of a bar chart about customers' dessert preference. Here's the data:
var desserts = ["Cookies","Cupcakes","Ice Cream Cones","Muffins"];
var customers = [34,41,52,18];
Let's start with a non-responsive chart. First, we set up our margins and prepare an SVG element to draw on:
var margin = {top: 10, right: 10, bottom: 120, left: 30};
var width = 300 - margin.left - margin.right;
var height = 300 - margin.top - margin.bottom;
//A non-responsive chart area
var chart = d3.select("#chart-div")
.append("svg")
.attr("width",width+margin.left+margin.right)
.attr("height",height+margin.top+margin.bottom)
.append("g")
.attr("transform","translate("+
margin.left+","+margin.top+")");
Now, we set up the scales and axes:
//Define the x and y scales
var x = d3.scale.ordinal()
.domain(desserts)
.rangePoints([0,width],1);
var y = d3.scale.linear()
.domain([0,60])
.range([height,0]);
//Define the x and y axes
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
Finally, add the axes and bars to our chart area:
//Add the x axis, rotate the labels
chart.append("g")
.attr("transform", "translate(0,"+height+")")
.call(xAxis)
.selectAll("text")
.attr("transform", "rotate(-65)")
.style("text-anchor", "end");
//Add the y axis
chart.append("g")
.call(yAxis);
//Add the bars
chart.selectAll("bars")
.data(desserts).enter()
.append("rect")
.attr("x",function (d) { return x(d)-20; })
.attr("y",function (d,i) { return y(customers[i]); })
.attr("height",function (d,i) { return height-y(customers[i]); })
.attr("width",50);
Here's our non-responsive chart in a JSFiddle. Notice the blue border around the div
element, and notice that the chart doesn't change if you resize the window.
Now the fun part! To make our chart responsive, we just need to change a few lines in our definition of the chart
area:
//A responsive chart area
var chart = d3.select("#chart-div")
.append("svg")
.attr("width","100%")
.attr("viewBox","0 0 "+
(width+margin.left+margin.right)+
" "+
(height+margin.top+margin.bottom) )
.append("g")
.attr("transform","translate("+
margin.left+","+margin.top+")");
Now we're defining the chart
area to be the width of our div
. Sometimes that width might be 100 pixels, and sometimes it might be 800 pixels. The viewBox
attribute tells the browser that--no matter the actual width of the chart
area--we want to work with a single coordinate system. Our coordinate system goes from 0 to width + margin.left + margin.right
in the x direction, and from 0 to height + margin.top + margin.bottom
in the y direction.
Here it is in a JSFiddle. Notice how the chart responds to width resizes, but how it will still outgrow the height of our div
.
What happens when we set both the width and height of chart
to be 100% or our div
? By default, the browser will preserve the aspect ratio of the drawing, so you will get the largest possible chart that fits into both the width and height of the div
. See it in this fiddle:
Sometimes we may want our chart to skew in order to fill the entire div
. In that case, we just add .attr("preserveAspectRatio","none")
to our definition of chart
. Here it is in a fiddle:
Although our example looked at a bar chart, the viewBox
technique works for many types of figures generated with D3.
Here's a great tutorial on the SVG viewBox: http://jonibologna.com/svg-viewbox-and-viewport/