Assume I have a histogram script that builds a 960 500 svg graphic. How do I make this responsive so on resize the graphic widths and heights are dynamic?
<script>
var n = 10000, // number of trials
m = 10, // number of random variables
data = [];
// Generate an Irwin-Hall distribution.
for (var i = 0; i < n; i++) {
for (var s = 0, j = 0; j < m; j++) {
s += Math.random();
}
data.push(s);
}
var histogram = d3.layout.histogram()
(data);
var width = 960,
height = 500;
var x = d3.scale.ordinal()
.domain(histogram.map(function(d) { return d.x; }))
.rangeRoundBands([0, width]);
var y = d3.scale.linear()
.domain([0, d3.max(histogram.map(function(d) { return d.y; }))])
.range([0, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.selectAll("rect")
.data(histogram)
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return height - y(d.y); })
.attr("height", function(d) { return y(d.y); });
svg.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", height)
.attr("y2", height);
</script>
Full example histogram gist is: https://gist.github.com/993912
If you are using d3.js through c3.js the solution to the responsiveness issue is quite straightforward :
where the generated HTML looks like :
In case people are still visiting this question - here’s what worked for me:
Enclose the iframe in a div and use css to add a padding of, say, 40% to that div (the percentage depending on the aspect ratio you want). Then set both width and height of the iframe itself to 100%.
In the html doc containing the chart to be loaded in the iframe, set width to the width of the div that the svg is appended to (or to the width of the body) and set height to width * aspect ratio.
Write a function that reloads the iframe content upon window resize, so as to adapt the size of the chart when people rotate their phone.
Example here on my website: http://dirkmjk.nl/en/2016/05/embedding-d3js-charts-responsive-website
UPDATE 30 Dec 2016
The approach I described above has some drawbacks, especially that it doesn’t take the height into account of any title and captions that are not part of the D3-created svg. I’ve since come across what I think is a better approach:
Example here on my website: http://dirkmjk.nl/en/2016/12/embedding-d3js-charts-responsive-website-better-solution
There's another way to do this that doesn't require redrawing the graph, and it involves modifying the viewBox and preserveAspectRatio attributes on the
<svg>
element:Update 11/24/15: most modern browsers can infer the aspect ratio of SVG elements from the
viewBox
, so you may not need to keep the chart's size up to date. If you need to support older browsers, you can resize your element when the window resizes like so:And the svg contents will be scaled automatically. You can see a working example of this (with some modifications) here: just resize the window or the bottom right pane to see how it reacts.
Look for 'responsive SVG' it is pretty simple to make a SVG responsive and you don't have to worry about sizes any more.
Here is how I did it:
The CSS code:
More info / tutorials:
http://demosthenes.info/blog/744/Make-SVG-Responsive
http://soqr.fr/testsvg/embed-svg-liquid-layout-responsive-web-design.php
I would avoid resize/tick solutions like the plague since they are inefficient and can cause issues in your app (e.g. a tooltip re-calculates the position it should appear on window resize, then a moment later your chart resizes too and the page re-layouts and now your tooltip is wrong again).
You can simulate this behaviour in some older browsers that don't properly support it like IE11 too using a
<canvas>
element which maintains it's aspect.Given 960x540 which is an aspect of 16:9:
I've coded up a small gist to solve this.
The general solution pattern is this:
I also added the minified d3.js script for speed. The gist is here: https://gist.github.com/2414111
jquery reference back code:
Debounce code:
Enjoy!