Journal

jQuery iFrame Sizing

19 January 2008 › 8 comments

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

iFrame: Example | Download (56 KB)

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.

Discussion + Dissension

  1. #1 Aaron Alexander

    Nathan, you’ve made the month of January ten times easier.

  2. #2 Gidge

    Elegant solution to an age old problem. Thanks for doing the dirty work for us, Nathan!

  3. #3 Matt

    Does Safari fire a resize event when it applies the CSS? It seems like it ought to.

    Come to think of it, you probably want to hook into onresize anyway, since inserting/removing/replacing any dynamic content that’s sitting in the iframe ought to trigger a resize of the iframe itself. Not to mention people changing the font size.

    Oh, internet.

  4. #4 Nathan Smith

    Unfortunately, I don’t think it’s really feasible to listen for an onresize event for the iframes. Most browsers don’t register a resize when text is changed, or Ajax causes the page to be redrawn. Unfortunately, because of browser limitations, I think this is as good as iframe sizing is going to get.

  5. #5 Dirk

    Unfortunately this doesn’t work for me in some cases, as i found out testing in my current project. I thought it would be nice for some cases, but instead it resized some of my iframes so small that no content was shown. The reason for this could be that three iframes are loaded during page load and all take some time to load, so the rendering size is probably not yet determined when the resizing fires. Oh well, no biggy, i can live without it.

  6. #6 Nathan Smith

    Dirk: Ah, I’m sorry to hear that. You might try putting setTimeout universally for the iframes in all browsers. It seems to me though, that the onload event should trigger correctly, despite the time it takes to load each page.

  7. #7 Dirk

    Nathan, maybe i will take a fresh look when i find some more time to do some experimenting. At least i used a part of your code to attach a onclick to all links in the iframes i have, which already helped a lot.

  8. #8 Bryan

    Nathan…fantastic work. I had no clue you could grab the height of a window from within an iframe…great work.

Comments closed after 2 weeks.

FYI


Member of 9rulesHosted by Mosso

Advertisement

Ads by SidebarAds

My Book

Textpattern Solutions I had the privilege of co-writing Textpattern Solutions for the web technology publisher Friends of ED. If you want to develop a professional dynamic web site, without the hassle of writing all the server-side code from scratch, then Textpattern could be just the solution you are looking for.

Latest Posts: All - RSS