[iframe] Is there a cross-domain iframe height auto-resizer that works?

I tried a few solutions but wasn't successful. I'm wondering if there is a solution out there preferably with an easy-to-follow tutorial.

This question is related to iframe cross-domain

The answer is


I ran into this issue while working on something at work (using React). Basically, we have some external html content that we save into our document table in the database and then insert onto the page under certain circumstances when you're in the Documents dataset.

So, given n inlines, of which up to n could contain external html, we needed to devise a system to automatically resize the iframe of each inline once the content fully loaded in each. After spinning my wheels for a bit, this is how I ended up doing it:

  1. Set a message event listener in the index of our React app which checks for a a specific key that we will set from the sender iframe.
  2. In the component that actually renders the iframes, after inserting the external html into it, I append a <script> tag that will wait for the iframe's window.onload to fire. Once that fires, we use postMessage to send a message to the parent window with information about the iframe id, computed height, etc.
  3. If the origin matches and the key is satisfied in the index listener, grab the DOM id of the iframe that we pass in the MessageEvent object
  4. Once we have the iframe, just set the height from the value that is passed from the iframe postMessage.
// index
if (window.postMessage) {
    window.addEventListener("message", (messageEvent) => {
        if (
            messageEvent.data.origin &&
            messageEvent.data.origin === "company-name-iframe"
        ) {
            const iframe = document.getElementById(messageEvent.data.id)
            // this is the only way to ensure that the height of the iframe container matches its body height
            iframe.style.height = `${messageEvent.data.height}px`
            // by default, the iframe will not expand to fill the width of its parent
            iframe.style.width = "100%"
            // the iframe should take precedence over all pointer events of its immediate parent
            // (you can still click around the iframe to segue, for example, but all content of the iframe
            // will act like it has been directly inserted into the DOM)
            iframe.style.pointerEvents = "all"
            // by default, iframes have an ugly web-1.0 border
            iframe.style.border = "none"
        }
    })
}
// in component that renders n iframes
<iframe
    id={`${props.id}-iframe`}
    src={(() => {
        const html = [`data:text/html,${encodeURIComponent(props.thirdLineData)}`]
        if (window.parent.postMessage) {
            html.push(
                `
                <script>
                window.onload = function(event) {
                    window.parent.postMessage(
                        {
                            height: document.body.scrollHeight,
                            id: "${props.id}-iframe",
                            origin: "company-name-iframe",
                        },
                        "${window.location.origin}"
                    );
                };
                </script>
                `
            )
        }

        return html.join("\n")
    })()}
    onLoad={(event) => {
        // if the browser does not enforce a cross-origin policy,
        // then just access the height directly instead
        try {
            const { target } = event
            const contentDocument = (
                target.contentDocument ||
                // Earlier versions of IE or IE8+ where !DOCTYPE is not specified
                target.contentWindow.document
            )
            if (contentDocument) {
                target.style.height = `${contentDocument.body.scrollHeight}px`
            }
        } catch (error) {
            const expectedError = (
                `Blocked a frame with origin "${window.location.origin}" ` +
                `from accessing a cross-origin frame.`
            )
            if (error.message !== expectedError) {
                /* eslint-disable no-console */
                console.err(
                    `An error (${error.message}) ocurred while trying to check to see ` +
                    "if the inner iframe is accessible or not depending " +
                    "on the browser cross-origin policy"
                )
            }
        }
    }}
/>

What I did was compare the iframe scrollWidth until it changed size while i incrementally set the IFrame Height. And it worked fine for me. You can adjust the increment to whatever is desired.

   <script type="text/javascript">
    function AdjustIFrame(id) {
        var frame = document.getElementById(id);
        var maxW = frame.scrollWidth;
        var minW = maxW;
        var FrameH = 100; //IFrame starting height
        frame.style.height = FrameH + "px"

        while (minW == maxW) {
            FrameH = FrameH + 100; //Increment
            frame.style.height = FrameH + "px";
            minW = frame.scrollWidth;
        }
    }

   </script>


<iframe id="RefFrame" onload="AdjustIFrame('RefFrame');" class="RefFrame"
    src="http://www.YourUrl.com"></iframe>

I found another server side solution for web dev using PHP to get the size of an iframe.

First is using server script PHP to an external call via internal function: (like a file_get_contents with but curl and dom).

function curl_get_file_contents($url,$proxyActivation=false) {
    global $proxy;
    $c = curl_init();
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($c, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7");
    curl_setopt($c, CURLOPT_REFERER, $url);
    curl_setopt($c, CURLOPT_URL, $url);
    curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
    if($proxyActivation) {
        curl_setopt($c, CURLOPT_PROXY, $proxy);
    }
    $contents = curl_exec($c);
    curl_close($c);
    $dom = new DOMDocument();
    $dom->preserveWhiteSpace = false;
    @$dom->loadHTML($contents);
    $form = $dom->getElementsByTagName("body")->item(0);
    if ($contents) //si on a du contenu
        return $dom->saveHTML();
    else
        return FALSE;
}
$url = "http://www.google.com"; //Exernal url test to iframe
<html>
    <head>
    <script type="text/javascript">

    </script>
    <style type="text/css">
    #iframe_reserve {
        width: 560px;
        height: 228px
    }
    </style>
    </head>

    <body>
        <div id="iframe_reserve"><?php echo curl_get_file_contents($url); ?></div>

        <iframe id="myiframe" src="http://www.google.com" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"  style="overflow:none; width:100%; display:none"></iframe>

        <script type="text/javascript">
            window.onload = function(){
            document.getElementById("iframe_reserve").style.display = "block";
            var divHeight = document.getElementById("iframe_reserve").clientHeight;
            document.getElementById("iframe_reserve").style.display = "none";
            document.getElementById("myiframe").style.display = "block";
            document.getElementById("myiframe").style.height = divHeight;
            alert(divHeight);
            };
        </script>
    </body>
</html>

You need to display under the div (iframe_reserve) the html generated by the function call by using a simple echo curl_get_file_contents("location url iframe","activation proxy")

After doing this a body event function onload with javascript take height of the page iframe just with a simple control of the content div (iframe_reserve)

So I used divHeight = document.getElementById("iframe_reserve").clientHeight; to get height of the page external we are going to call after masked the div container (iframe_reserve). After this we load the iframe with its good height that's all.


Here is an alternative implementation.

enter image description here

Basically if you able to edit page at other domain you can place another iframe page that belongs to your server which saving height to cookies. With an interval read cookies when it is updated, update the height of the iframe. That is all.

Edit: 2019 December

The solution above basically uses another iframe inside of an iframe 3rd iframe is belongs to the top page domain, which you call this page with a query string that saves size value to a cookie, outer page checks this query with some interval. But it is not a good solution so you should follow this one:

In Top page :

window.addEventListener("message", (m)=>{iframeResizingFunction(m)});

Here you can check m.origin to see where is it comes from.

In frame page:

window.parent.postMessage({ width: 640, height:480 }, "*")

Although, please don't forget this is not so secure way. To make it secure update * value (targetOrigin) with your desired value. Please follow documentation: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage


I have a script that drops in the iframe with it's content. It also makes sure that iFrameResizer exists (it injects it as a script) and then does the resizing.

I'll drop in a simplified example below.

// /js/embed-iframe-content.js

(function(){
    // Note the id, we need to set this correctly on the script tag responsible for
    // requesting this file.
    var me = document.getElementById('my-iframe-content-loader-script-tag');

    function loadIFrame() {
        var ifrm = document.createElement('iframe');
        ifrm.id = 'my-iframe-identifier';
        ifrm.setAttribute('src', 'http://www.google.com');
        ifrm.style.width = '100%';
        ifrm.style.border = 0;
        // we initially hide the iframe to avoid seeing the iframe resizing
        ifrm.style.opacity = 0;
        ifrm.onload = function () {
            // this will resize our iframe
            iFrameResize({ log: true }, '#my-iframe-identifier');
            // make our iframe visible
            ifrm.style.opacity = 1;
        };

        me.insertAdjacentElement('afterend', ifrm);
    }

    if (!window.iFrameResize) {
        // We first need to ensure we inject the js required to resize our iframe.

        var resizerScriptTag = document.createElement('script');
        resizerScriptTag.type = 'text/javascript';

        // IMPORTANT: insert the script tag before attaching the onload and setting the src.
        me.insertAdjacentElement('afterend', ifrm);

        // IMPORTANT: attach the onload before setting the src.
        resizerScriptTag.onload = loadIFrame;

        // This a CDN resource to get the iFrameResizer code.
        // NOTE: You must have the below "coupled" script hosted by the content that
        // is loaded within the iframe:
        // https://unpkg.com/[email protected]/js/iframeResizer.contentWindow.min.js
        resizerScriptTag.src = 'https://unpkg.com/[email protected]/js/iframeResizer.min.js';
    } else {
        // Cool, the iFrameResizer exists so we can just load our iframe.
        loadIFrame();
    }    
}())

Then the iframe content can be injected anywhere within another page/site by using the script like so:

<script
  id="my-iframe-content-loader-script-tag"
  type="text/javascript"
  src="/js/embed-iframe-content.js"
></script>

The iframe content will be injected below wherever you place the script tag.

Hope this is helpful to someone.


I got the solution for setting the height of the iframe dynamically based on it's content. This works for the cross domain content. There are some steps to follow to achieve this.

  1. Suppose you have added iframe in "abc.com/page" web page

    <div> <iframe id="IframeId" src="http://xyz.pqr/contactpage" style="width:100%;" onload="setIframeHeight(this)"></iframe> </div>

  2. Next you have to bind windows "message" event under web page "abc.com/page"

window.addEventListener('message', function (event) {
//Here We have to check content of the message event  for safety purpose
//event data contains message sent from page added in iframe as shown in step 3
if (event.data.hasOwnProperty("FrameHeight")) {
        //Set height of the Iframe
        $("#IframeId").css("height", event.data.FrameHeight);        
    }
});

On iframe load you have to send message to iframe window content with "FrameHeight" message:

function setIframeHeight(ifrm) {
   var height = ifrm.contentWindow.postMessage("FrameHeight", "*");   
}
  1. On main page that added under iframe here "xyz.pqr/contactpage" you have to bind windows "message" event where all messages are going to receive from parent window of "abc.com/page"
window.addEventListener('message', function (event) {

    // Need to check for safety as we are going to process only our messages
    // So Check whether event with data(which contains any object) contains our message here its "FrameHeight"
   if (event.data == "FrameHeight") {

        //event.source contains parent page window object 
        //which we are going to use to send message back to main page here "abc.com/page"

        //parentSourceWindow = event.source;

        //Calculate the maximum height of the page
        var body = document.body, html = document.documentElement;
        var height = Math.max(body.scrollHeight, body.offsetHeight,
            html.clientHeight, html.scrollHeight, html.offsetHeight);

       // Send height back to parent page "abc.com/page"
        event.source.postMessage({ "FrameHeight": height }, "*");       
    }
});

You have three alternatives:

1. Use iFrame-resizer

This is a simple library for keeping iFrames sized to their content. It uses the PostMessage and MutationObserver APIs, with fall backs for IE8-10. It also has options for the content page to request the containing iFrame is a certain size and can also close the iFrame when your done with it.

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

2. Use Easy XDM (PostMessage + Flash combo)

Easy XDM uses a collection of tricks for enabling cross-domain communication between different windows in a number of browsers, and there are examples for using it for iframe resizing:

http://easyxdm.net/wp/2010/03/17/resize-iframe-based-on-content/

http://kinsey.no/blog/index.php/2010/02/19/resizing-iframes-using-easyxdm/

Easy XDM works by using PostMessage on modern browsers and a Flash based solution as fallback for older browsers.

See also this thread on Stackoverflow (there are also others, this is a commonly asked question). Also, Facebook would seem to use a similar approach.

3. Communicate via a server

Another option would be to send the iframe height to your server and then poll from that server from the parent web page with JSONP (or use a long poll if possible).