frontend, Locastic May 05

Dynamic adjustment of the iframe height

2 min read –

NOTE: As far as I know there is no one-sided solution for this problem – it can’t be done with just the iframe or just the website, you need to have access to both iframe code and the site hosting it.

A while ago, our client noticed that the iframe does not show scroll, so it’s impossible to know there is a register button in their app iframe.

The idea was to always show the scroll bar using CSS or something like this. And it worked everywhere… except iOS 14 🤦 , because custom scrollbars are no longer supported in iOS 14.

Since there was no way to indicate to our users that the iframe is scrollable we decided to adjust the iframe height dynamically based on its content.

window.postMessage to the rescue

// Iframe
function resize() {
    var height = $(html).height();

    // Backwards – send message to parent
    window.parent.postMessage(['setHeight', height], '*');
}
// Parent
window.addEventListener('message', function (e) {
    var $iframe = $('#the-iframe');

    var eventName = e.data[0];
    var data      = e.data[1];

    switch (eventName) {
        case 'setHeight':
            $iframe.height(data);
            break;
        ...
    }
}, false);

This is the basic structure: iframe can via window.parent.postMesage let the parent (i.e. the website hosting it) know its size, and the parent gets it via the window.addEventListener(‘message’ …

In our specific case it looked like this:

// iframe
let height;
 
const sendPostMessage = () => {
// to determine our id
   var params = getQueryParams(window.location.href); 
// if height has changed (new content or screen resize)
   if (height !== document.getElementsByClassName('iframe-wrapper')[0].offsetHeight) {
// determine the height 
     height = document.getElementsByClassName('iframe-wrapper')[0].offsetHeight;
// and send it to the parent
     window.parent.postMessage({
       frameHeight: height,
       frameId: params.id 
     }, '*');
     // console.log(height); // just for testing purposes
   }
 }
 
 window.onload = () => sendPostMessage();
 window.onresize = () => sendPostMessage();
// website

window.onmessage = (e) => {
// make sure its the message we need
  if (e.data.hasOwnProperty("frameHeight") && e.data.hasOwnProperty("frameId")) {
// to avoid retroactively adding/using ids on iframes
    var frameovi = document.getElementsByTagName('iframe');
    for (let frame of frameovi) {
      let src = frame.getAttribute('src');
 // in the case of lazy-loading
      let dataSrc = frame.getAttribute('data-src');
      if(
          (src && src.includes(`iframe?id=${e.data.frameId}`))
          || (dataSrc && dataSrc.includes(`iframe?id=${e.data.frameId}`))
      ) {
// adjust the height 
        frame.style.height = `${e.data.frameHeight + 60}px`;
      }
    }
  }
};

Notice that you can use this pattern to send any sort of information from the iframe to the parent. In case you’d like to find some additional amazing iframe features, make sure you check Ben Vinegar’s seamless talk.