jQuery iFrame Sizing & auto-sizing iframes

Note: Due to browser security limitations related to cross-site scripting, this solution works only for iframe page content called from the same domain.


Example http://host.sonspring.com/iframe/

Download: http://sonspring.com/journal/jquery-iframe-sizing





First off, let me say that frames are bad and should be avoided. That being said, if you must use frames then I suppose the iframe would be the least of all the evils. Occasionally, project requirements arise such that you simply cannot get around having to use these pesky elements.

One thing I have always detested about iframes is they are not aware of the dimensions of their content. Setting the width of an iframe is trivial, because you generally know how wide its contents will be. The pain point often comes when specifying height, because pages always vary in length.

With a fixed height, the iframe is either too tall or short. This causes one of two undesirable outcomes: cutoff content, or a scrollbar. One of the cardinal sins of design is to have multiple scrollbars per page, and cutoff content just looks awkward. These two factors make iframes nearly unusable.

This past week, I thought to myself: “Wouldn’t it be nice if iframes just auto-magically knew what height they needed to be?” After a few days of toiling, with the help of jQuery, auto-sizing iframes are a reality. The underlying JavaScript code is actually quite simple.

// For other good browsers.
$('iframe').load(function() {
  this.style.height =
  this.contentWindow.document.body.offsetHeight + 'px';
});

This attaches an onload event listener to each iframe tag within the page. As the content for each one loads, it reports its overall body height, and the iframes then size themselves accordingly. Likewise, any link clicked that causes the iframe to reload, also triggers a height recalculation.

In web development, not all browsers do things correctly. In this case, the culprits are Safari and Opera. They prematurely report the onload event as being completed, once the DOM is in place, but before the CSS renders. This gets the height calculated with browser default styles (16px font, etc) rather than the true on-screen representation.

Roll up your sleeves folks, because things are about to get messy. In order to wrangle Safari and Opera under control, we put the height calculations in a separate chunk. True to the Apple spirit, I’ve named my function iResize.

I then watch for onload, but instead of calling the resizing code directly, I call it via the native JS function setTimeout. This forces Safari and Opera to pause for an infinitesimal amount of time, enough to realize the CSS has rendered for all the iframes. After this, the correct height is retrieved.

// Start timer when loaded.
$('iframe').load(function() {
  setTimeout(iResize, 0);
});

You’d think this would be enough, but oh how Safari and Opera love to play coy! They do not actually execute the code associated with onload when the entire parent page is loaded. Instead, it takes reloading the content of each iframe individually for anything to happen.

Undeterred, we need to smack some sense into these browsers. We grab the src attributes of all the iframes, and save that array into a variable iSource. We take their lunch money by setting the src attributes to blank, after which we immediately reassign the values stored in iSource. After this slight of hand, Safari and Opera finally render correctly.

// Safari and Opera need a kick-start.
for (var i = 0, j = iFrames.length; i < j; i++) {
  var iSource = iFrames[i].src;
  iFrames[i].src = '';
  iFrames[i].src = iSource;
}

It’s worth mentioning there is some old-school HTML in play, for Internet Explorer and Opera. Unless otherwise specified, IE will produce a nasty border, necessitating frameborder. Like Safari, Opera tends to calculate height prematurely, but scrolling negates the issue.

<iframe src="…" scrolling="no" frameborder="0"></iframe>

There you have it folks, a reliable cross-browser solution to the iframe predicament. Just include the iframe.js file, following jquery.js, in the head of your document and it will automatically parse all iframe tags and resize them accordingly. Hopefully this will save some developer headaches.

Comments

Popular posts from this blog