Embedding D3.js charts in a responsive website - a better solution

30 December 2016

I updated the code so it doesn’t need jQuery.

I often use D3.js to create charts which I embed on my website (the chart below is included merely as an illustration; it was copied from here). Normally you set the width and height of the embedded page in the embed code, but with a responsive layout it’s not so simple. The challenge is to adapt the iframe width to varying screen sizes and change the height so that the chart still fits.

After struggling with this issue for quite a while I thought I had come across the solution and wrote an article about it. However, this solution has two problems:

A while ago, I came across a different approach which uses HTML5’s postMessage. The embedded page posts a message containing it’s own height to the parent page. The parent page picks up the message and changes the iframe height accordingly.

A smart variant has the embedded page not only send its height, but also its url to the parent page. That way, you can identify the corresponding iframe by its src attribute and thus make sure the right iframe gets updated - which is nice if you have more than one iframe on a web page.

Here’s how it works. In the D3 code, set the width of the chart to the width of the div the svg is attached to and use the aspect ratio to calculate the chart height. Also add the following code to the embedded page. It will send its height and url to the parent page:

function sendHeight() {
    var body = document.body;
    var html = document.documentElement;
    var height = Math.max(body.scrollHeight, body.offsetHeight,
                 html.clientHeight, html.scrollHeight, html.offsetHeight );
    window.parent.postMessage({
        'height': height,
        'location': window.location.href
  }, "*");
}

window.addEventListener('load', function () {
  sendHeight();
});
And here’s the code for the parent page. It will pick up the message, identify the corresponding iframe and update its height:
window.addEventListener('message', function(event) {
    if (event.origin !== 'https://dirkmjk.nl' & event.origin !== window.location.origin) return;
    var data = event.data;
    var height = data.height + 32;
    var src = data.location.substring(event.origin.length);
    document.querySelector("iframe[src*='" + src + "']").style.height = height + 'px';
}, false);

In the second line, the domain should be replaced with the domain where the embedded page is hosted (the line checks for the origin of the posted message for security reasons). You could add a check whether the height value is in fact a number.

I haven’t extensively checked this but it works on iOS and Android. Since it uses postMessage, it will not work on some older browsers. Then again, D3.js won’t work on some older browsers either.

Credits go to thomax and Jan Werkhoven.

30 December 2016 | Categories: d3js, data