Redux: Set IFRAME height based on size of remotely loaded content

Shortly after writing a quick guide on how to set IFRAME height based on size of remotely loaded content an error came to light where Chrome and Firefox would either keep growing the iframe with after each load, or would set the iframe too large if subsequent content was shorter than the initial content.

I wasn't able to determine the exact cause of the problem, but I implemented a work around which cured the problem. I've extracted the core code as a template to resolve the problem. Basically we call a function periodically, and try to see if the IFRAME height remains constant. If it does, then assume it's stopped changing, and call the local resizing function with the hopefully correct height.

REMOTE.COM/REMOTE.HTML

var IFRAME={};
function isHeightStatic(h, x){
	IFRAME.resizeHeight[IFRAME.resizeHeight.length] = h;

	// Test to see if h has changed over last x iterations.
	for (var i=IFRAME.resizeHeight.length-1; i >= IFRAME.resizeHeight.length-x; i--)  // don't test last element, as we just added it
		if (i < 0 || IFRAME.resizeHeight[i]!=h) return false;  //value is or may still be changing
	return true;
}
function iframeResizePipe(){
	// noticable when subsequent content is shorter.
	//var height = jQuery(document.body).height();
	var height = document.body.parentNode.scrollHeight;
	var heightStatic = isHeightStatic(height,4);  //same height for 1 second? (4 * 250ms)
	if (IFRAME.resizeHeight.length>48 || heightStatic) //Iterate 50 times at 250ms delay = 12 seconds load time
		clearInterval(IFRAME.resizeTimer);

	if (!heightStatic){
		// Going to 'pipe' the data to the parent through the helpframe..
		var pipe = document.getElementById('helpframe');
		if ( pipe == undefined ) return;
		// Cachebuster a precaution here to stop browser caching interfering
		pipe.src = 'http://local.com/helper.html?height='+height+'&cacheb='+Math.random();
	}
}
jQuery(document.body).ready( function(){
	// Ready event seems to trigger before remote images are loaded. When images don't speficy height,
	// cause page height to be calculated wrongly (from Larger Images).
	// So ping every 250ms to see if the height has increase, and reset the iframe size.
	IFRAME.resizeTimer = setInterval('iframeResizePipe()', 250);
});

NOTE: Code extracted from working code, based around jQuery, which has been commented out, with the exception of the onload handler which is performed by jQuery above.

Hope this helps a little at least.

Comments

  1. By Stan, on January 09, 2011, at 05:54 PM
    Hi David,

    I got your original code working great, except for the sizing bug, which you have a fix here.

    Having trouble with one piece of the code, tho, maybe you could help me out? (Not done much JS, but love what you did with the IFrames).

    Had a question about this line of code: IFRAME.resizeHeight[IFRAME.resizeHeight.length]

    What kind of object is IFRAME? Interpreter doesn't seem to like the resizeHeight.

    Many thanks,
    Stan
  2. By David Gilbert, on January 13, 2011, at 06:06 PM
    IFRAMES are defined by w3. They are accessible from javascript in a number of ways. In this case the iframe is being accessed through as an array. It's possible you are hitting into this issue though.
  3. By OG, on January 24, 2011, at 11:37 PM
    Hi, thanks for your brilliant code. However, I still cannot make it to work in Firefox (still has the growing issue). As I'm not familiar with web scripting, this might be a stupid question but, do I just wrap <script type="text/javascript"></script> around the whole code above for it to work (replace the original script inside the remote.html with this one)? I tried doing that but now, the whole thing doesn't work and IE shows this error:

    IFRAME.resizeHeight.length is null or not an object

    I know I might be doing something wrong. But I'm wondering what it is.
  4. By Patrick Sudderth, on August 11, 2011, at 04:47 PM
    David,

    First off... Congrats on this great code. I was really struggling with this until I found your blog... Many thanks!

    However, I too am having issues with this Redux code... Like "OG" above, I replaced the script in remote.html with the code above and it's not working... I added jquery 1.6.2 to the head and also tried it with an older version, just in case. I messed around with this quite a bit and had little success... I'm not the most seasoned javascript guy so I'm sure I missed something obvious. Any help would be much appreciated...

    All browsers are kicking back similar issues:

    IE9: SCRIPT5007: Unable to get value of the property 'length': object is null or undefined
    test.html, line 23 character 2

    Chrome14: Uncaught TypeError: Cannot read property 'length' of undefined
  5. By Justin, on October 19, 2011, at 08:18 PM
    I'm also hitting the 'IFRAME.resizeHeight' bug. FF 7.
  6. By Koen Heye, on December 23, 2011, at 05:55 PM
    Hello everyone!

    I also encountered the growing IFrame problem in Firefox (Chrome was ok). And first tried the suggested solution in this post. But it didn't quite work and it's pretty javascript intensive with the use of an interval.

    After some digging around I came across a thread which pointed me in the right direction: http://www.daniweb.com/web-development/javascript-dhtml-ajax/threads/71494/1186132#post1186132

    In the IFrameResizePipe method I now don't get the scrollHeight of the parentNode but use offsetHeight on an wrapper element on the page. In the remote.html page I added a div which just acts as a wrapper. In the javascript below, the id of my wrapper element is 'container'.

    So instead of: var height = document.body.parentNode.scrollHeight;
    Use: var height = document.getElementById('container').offsetHeight;

    Here is the full javascript I use on my remote.html page.
    <script type="text/javascript">
    function iframeResizePipe() {
    var height = document.getElementById('container').offsetHeight;

    // Going to 'pipe' the data to the parent through the helpframe..
    var pipe = document.getElementById('helpframe');
    if (pipe == undefined) return;
    // Cachebuster a precaution here to stop browser caching interfering
    pipe.src = 'http://local.com/helper.html?height=' + height + '&cacheb=' + Math.random();
    }
    </script>
    And this is how my html body looks like:
    <body onload="iframeResizePipe()">
    <div id="container">
    <!-- Here comes your remote content. -->
    </div>
    <iframe id="helpframe" src='' height='0' width='0' frameborder='0'></iframe>
    </body>

    I hope this helps anyone who comes across this site and post. And David, thanks for your great article back in 2009: http://solidgone.org/Set-IFRAME-height-based-on-size-of-remotely-loaded-content

    Koen