Embedding D3.js charts in a responsive website
UPDATE - better approach here.
For a number of reasons, I like to use D3.js for my charts. However, I’ve been struggling for a while to get them to behave properly on my blog which has a responsive theme. I’ve tried quite a few solutions from Stack Overflow and elsewhere but none seemed to work.
I want to embed the chart using an iframe. The width of the iframe should adapt to the column width and the height to the width of the iframe, maintaining the aspect ratio of the chart. The chart itself should fill up the iframe. Preferably, when people rotate their phone, the size of the iframe and its contents should update without the need to reload the entire page.
Styling the iframe
Smashing Magazine has described a solution for embedding videos. You 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). You can then set both width and height of the iframe itself to 100%. Here’s an adapted version of the code:
<style>
.container_chart_1 {
position: relative;
padding-bottom: 40%;
height: 0;
overflow: hidden;
}
.container_chart_1 iframe {
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
</style>
<div class ='container_chart_1'>
<iframe src='https://dirkmjk.nl/files/articles/2016/embed_d3/chart_1.html' frameborder='0' scrolling = 'no' id = 'iframe_chart_1'>
</iframe>
</div>
Making the chart adapt to the iframe size
The next question is how to make the D3 chart adapt to the dimensions of the iframe. Here’s what I thought might work but didn’t: in the chart, obtain the dimensions of the iframe using window.innerWidth
and window.innerHeight
(minus 16px - something to do with scrollbars apparently?) and use those to define the size of your chart.
Using innerWidth
and innerHeight
seemed to work - until I tested it on my iPhone. Upon loading a page it starts out OK, but then the update function increases the size of the chart until only a small detail is visible in the iframe (rotate your phone to replicate this). Apparently, iOS returns not the dimensions of the iframe but something else when innerWidth
and innerHeight
are used. I didn’t have that problem when I tested on an Android phone.
Adapt to the iframe size: Alternative solution
Here’s an alternative approach for making the D3 chart adapt to the dimensions of the iframe. Set width to the width of the div
that the chart is appended to (or to the width of the body) and set height to width * aspect ratio. Here’s the relevant code:
var aspect_ratio = 0.4;
var frame_width = $('#chart_2').width();
var frame_height = aspect_ratio * frame_width;
The disadvantage of this approach is that you’ll have to set the aspect ratio in two places: both in the css for the div
containing the iframe and in the html-page that is loaded in the iframe. So if you decide to change the aspect ratio, you’ll have to change it in both places. Other than that, it appears to work.
Reloading the chart upon window resize
Then 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. Note that on mobile devices, scrolling may trigger the window resize. You don’t want to reload the contents of the iframe each time someone scrolls the page. To prevent this, you may add a check whether the window width has changed (a trick I picked up here). Also note that with Drupal, you need to use jQuery
instead of $
.
width = jQuery(window).width;
jQuery(window).resize(function(){
if(jQuery(window).width() != width){
document.getElementById('iframe_chart_1').src = document.getElementById('iframe_chart_1').src;
width = jQuery(window).width;
}
});
In case you know a better way - do let me know!
FYI, here’s the chart used as illustration in its original context.