Search code examples
javascripthtmljqueryiframeheight

iframe Auto Adjust Height as content changes


I have an iframe as you can see on the following link;-

http://one2onecars.com

The iframe is the online booking in the centre of the screen. The problem I have is that although the height of the iframe is okay as the page loads, I need it to somehow auto adjust the height as the page content adjusts. For example, if I do a postcode search in the online booking it creates a dropdown menu and then makes the 'Next Step' button not viewable.

What I need to happen is that when the content of the online booking changes, the iframe auto adjusts to the new height of the iframe (dynamically) as it is not loading any other pages.

I have tried several different scripts using jquery to try resolving this issue, but they all only seem to auto adjust the height of the iframe when the page first loads and not as the contents of the iframe changes.

Is this even possible to do?

The code I have at the moment is with a set height at the moment:-

        <div id="main-online-booking">

            <iframe id="main-online-frame" class="booking-dimensions" src="http://www.marandy.com/one2oneob/login-guest.php" scrolling="no" frameborder="0"></iframe>

        </div>

#main-online-booking {
    height: 488px;
    border-bottom: 6px #939393 solid;
    border-left: 6px #939393 solid;
    border-right: 6px #939393 solid;
    z-index: 4;
    background-color: #fff;
}


.booking-dimensions {
    width: 620px;
    height: 488px;
}

If anybody can help me with this I would be much appreciated!


Solution

  • Modern browsers have some new features that make this task easier than it use to be.

    PostMessage

    The postMessage API provides a simple method for comunicating between an iFrame and its parent.

    To send a message to the parent page you call it as follows.

    parent.postMessage('Hello parent','http://origin-domain.com');
    

    In the other direction we can send the message to the iFrame with the following code.

    var iframe = document.querySelector('iframe');
    iframe.contentWindow.postMessage('Hello my child', 'http://remote-domain.com:8080');
    

    To recevie a message create an event listerner for the message event.

    function receiveMessage(event)
    {
      if (event.origin !== "http://remote-domain.com:8080")
        return;
    
      console.log(event.data);
    }
    
    if ('addEventListener' in window){
        window.addEventListener('message', receiveMessage, false);
    } else if ('attachEvent' in window){ //IE
        window.attachEvent('onmessage', receiveMessage);
    

    These examples uses the origin property to limit where the message is sent to and to check where it came from. It is possible to specify * to allow sending to any domain and you may in some cases you may want to accept messages from any domain. However, if you do this you need to consider the security implications and implement your own checks on the incoming message to ensure it contains what your expecting. In this case the iframe can post its height to '*', as we might have more than one parent domain. However, it's a good idea to check incoming messages are from the iFrame.

    function isMessageFromIFrame(event,iframe){
        var
            origin  = event.origin,
            src     = iframe.src;
    
        if ((''+origin !== 'null') && (origin !== src.substr(0,origin.length))) {
            throw new Error(
                'Unexpect message received from: ' + origin +
                ' for ' + iframe.id + '. Message was: ' + event.data  
            );
        }
    
        return true;
    }
    

    MutationObserver

    The other advance in more modern broswers is MutationObserver which allows you to watch for changes in the DOM; so it is now possible to detect changes that could affect the size of the iFrame without having to constantly poll with setInterval.

    function createMutationObserver(){
        var
            target = document.querySelector('body'),
    
            config = {
                attributes            : true,
                attributeOldValue     : false,
                characterData         : true,
                characterDataOldValue : false,
                childList             : true,
                subtree               : true
            },
    
            observer = new MutationObserver(function(mutations) {
                parent.postMessage('[iframeResize]'+document.body.offsetHeight,'*');
            });
    
        log('Setup MutationObserver');
        observer.observe(target, config);
    }
    
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    
    if (MutationObserver){
        createMutationObserver();
    }
    

    Working out an accurate height

    Getting an accurate height for the iFrame is not as simple as it should be, as you have a choice of six different properties that you can check and none of them give a constantly right answer. The best solution I've come up with is this function that works so long as you don't use CSS to overflow the body tag.

    function getIFrameHeight(){
        function getComputedBodyStyle(prop) {
            const style = document.defaultView.getComputedStyle(
                document.body, null);
            return parseInt(style.getPropertyValue(prop), 10);
        }
    
        return document.body.offsetHeight +
            getComputedBodyStyle('marginTop') +
            getComputedBodyStyle('marginBottom');
    }
    

    This is the IE9 version, for the much long IE8 version see this answer.

    If you do overflow the body and you can't fix your code to stop this, then using either the offsetHeight or scrollHeight properties of document.documentElement are your best options. Both have pros and cons and it is best just to test both and see which works for you.

    Other issues

    Other things to consider include: having more than one iFrame on the page, CSS :Checkbox and :Hover events causing page resize, avoiding the use of height auto in the iFrames' body and html tags, and lastly the window being resized.

    IFrame Resizer Library

    I've wrapped all this up in a simple dependancy free library, that also provides some extra functions and page size detection algorithms not discussed here.

    https://github.com/davidjbradshaw/iframe-resizer

    This works in all modern browsers.