Creating responsive iframes that adjust to content height

I remember quite a few years ago working with iframes and trying to get the source page within an iframe to communicate with the page the iframe was located. At the time we failed because it wasn’t possible for the two pages to communicate with one another.

Fast-forward a number of years and I’d been asked to design and build a quiz that is embeddable on other websites; back working with iframes. As I try to build everything responsive this was no different but the problem was that while an iframe can be set to 100% width it’s height has to be fixed and with the height of the quiz likely to change at different widths as well as when you progress from question to answer we needed a way to make the height react to the height of the iframe contents.

Step forward postMessage()

postMessage allows safe cross-origin (different domains) communication. This method allows our quiz to talk to any page where this is embedded even when the hosting domain or protocol is different.

By being able to communicate in this way we can calculate the height of our page and post this to the embed page which can then use it to size the iframe to fit the content.

Here’s how to do it…

Lets start with our quiz page.

<body>
  <div class="wrapper">
    <h1>Question</h1>
    <ol>
      <li>Answer 1</li>
      <li>Answer 2</li>
      <li>Answer 3</li>
    </ol>
  </div>
</body>

Simple so far. Now I’ll leave you to imagine the form, the different pages and the AJAX that submits the form rather than requiring multiple page refreshes. The main bit we need is the wrapper class that allows us to know the exact height of all the contents on the page.

Next we create the function to get the height of the page and use the postMessage method. That last * can be replaced for a url if you only want to give specific domains access to the message but as we don’t know where the quiz might be embedded we use the asteriks wildcard. Read more about postMessage here.

function sendHeight(){
  var actual_height = document.querySelector('.wrapper').scrollHeight;
  var pass_data = {
    'contentheight':actual_height
  }
  parent.postMessage(JSON.stringify(pass_data),"*");
}

Here we create a JSON array so that we can send more than just one piece of information in the postMessage if we wished. One such example might be a flag to differentiate first and subsequent loads as on subsequent ones you may wish to scroll users back to the top of the iframe when this is off screen.

In addition to calling out sendHeight function when we get a successful ajax callback we also call it on load so that then the page first loads the iframe height is right at the start.

window.onload = function(){
  sendHeight();
}

Next, the embed code. You might expect the following code to either be minified or loaded through another file via a <script> tag depending on how short you want your embed code to be. We wen down the minification route and so start with a simple <div>.

<div id="iframe--wrap--123456"></div>

This is all the HTML we want as we’ll use JavaScript to create the iframe. The id for the <div> should be as unique as possible so that none of the CSS from the parent site is likely to affect this.

Next we create our iframe with JavaScript…

var iFrameWrap = document.getElementById('iframe--wrap--123456');

document.addEventListener("DOMContentLoaded", function(){
  iFrameWrap.style.cssText = 'height:0;margin:20px auto;overflow:hidden;padding-bottom:200px;position:relative;';
  iFrameWrap.innerHTML = '<iframe src="http://www.bronco.co.uk/quiz.php" allowfullscreen frameborder="0" scrolling="no" style="height:100%;left:0;overflow:hidden;position:absolute;top:0;width:100%;" id="iframe--wrap--123456--iframe">';
});

Our JavaScript only runs after the page has loaded so we’re not adversely affecting the loading of the website. The CSS you’ll notice actually has the iframe as position:absolute with height:100% so it’s the <div> we’ll actually be affecting the height of and instead of setting the height of the <div> we’ll adjust the padding-bottom too. You’ll see by default we set padding-bottom to 200px, this is just a random choice and could be anything you wish.

Honest moment: I can’t remember why we’re adjusting padding and the height of a div rather than the iframe but it’s weird enough to know I didn’t do it by default so it’s probably neccessary to fix a particular bug or to achieve the result we need.

// LISTENER FOR IFRAME HEIGHT
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

// LISTEN TO MESSAGE FROM CHILD WINDOW
eventer(messageEvent,function(e){
  var pass_data = JSON.parse(e.data);
  if(pass_data.contentheight){
    resizeIFrameToFitContent(pass_data.contentheight);
  }
},false);

// RESIZE IFRAME
function resizeIFrameToFitContent(height){
  iFrameWrap.style.paddingBottom = height + 'px';
}

The above code also goes onto our embed page and is listening for messageEvent that is the other half to postMessage. Our functions take the JSON array apart and uses the height figure we have in there to make the iframe container <div> the height we want by adjusting it’s padding-bottom.

That’s it. It’s all fairly basic JavaScript with postMessage the only bit of code that might be new to some. Hopefully it’ll help people avoid being restricted to creating fixed height embeddable content.


We'd love to hear from you!

If you think Bronco has the skills to take your business forward then what are you waiting for?

Get in Touch Today!

Discussion

Add a Comment