Using Iframes Sparingly
This post is based on a chapter from Even Faster Web Sites, the follow-up to High Performance Web Sites. Posts in this series include: chapters and contributing authors, Splitting the Initial Payload, Loading Scripts Without Blocking, Coupling Asynchronous Scripts, Positioning Inline Scripts, Sharding Dominant Domains, Flushing the Document Early, Using Iframes Sparingly, and Simplifying CSS Selectors.
Time to create 100 elements
Iframes provide an easy way to embed content from one web site into another. But they should be used cautiously. They are 1-2 orders of magnitude more expensive to create than any other type of DOM element, including scripts and styles. The time to create 100 elements of various types shows how expensive iframes are.
Pages that use iframes typically don’t have that many of them, so the DOM creation time isn’t a big concern. The bigger issues involve the onload event and the connection pool.
Iframes Block Onload
It’s important that the window’s onload event fire as soon as possible. This causes the browser’s busy indicators to stop, letting the user know that the page is done loading. When the onload event is delayed, it gives the user the perception that the page is slower.
The window’s onload event doesn’t fire until all its iframes, and all the resources in these iframes, have fully loaded. In Safari and Chrome, setting the iframe’s SRC dynamically via JavaScript avoids this blocking behavior.
One Connection Pool
Browsers open a small number of connections to any given web server. Older browsers, including Internet Explorer 6 & 7 and Firefox 2, only open two connections per hostname. This number has increased in newer browsers. Safari 3+ and Opera 9+ open four connections per hostname, while Chrome 1+, IE 8, and Firefox 3 open six connections per hostname. You can see the complete table in my Roundup on Parallel Connections.
One might hope that an iframe would have its own connection pool, but that’s not the case. In all major browsers, the connections are shared between the main page and its iframes. This means it’s possible for the resources in an iframe to use up the available connections and block resources in the main page from loading. If the contents of the iframe are as important, or more important, than the main page, this is fine. However, if the iframe’s contents are less critical to the page, as is often the case, it’s undesirable for the iframe to commandeer the open connections. One workaround is to set the iframe’s SRC dynamically after the higher priority resources are done downloading.
5 of the 10 top U.S. web sites use iframes. In most cases, they’re used for ads. This is unfortunate, but understandable given the simplicity of using iframes for inserting content from an ad service. In many situations, iframes are the logical solution. But keep in mind the performance impact they can have on your page. When possible, avoid iframes. When necessary, use them sparingly.
Martin Borthiry | 04-Jun-09 at 4:02 am | Permalink |
Very good article!
I think that it is possible to solve the adsense problem. You only have to set an ID to adsense script and remove the SRC atribute. Then, put a new script to set the SRC of the ads script, on the bottom page. Something like this:
url = ‘http://pagead2.googlesyndication.com/pagead/show_ads.js’;
document.getElementById(’ads_script’).src = url;
Thanks for share your knowledge.
Kirk Cerny | 04-Jun-09 at 3:36 pm | Permalink |
Great Post.
Danny Khatib | 04-Jun-09 at 3:50 pm | Permalink |
We have a similar problem where we render three ads on a page via Google Ad Manager, and have wrapped each partner ad code in iframes (served from our domain) to sandbox the code. we thought it was asynchronously loading the html snippet and js, but not necessarily executing in parallel. the performance is better but still not good at all. it’s a big problem facing many of us.
we’re considering this option for performance improvement:
– put Google Ad Manager initialization code at the bottom (just inside body tag) instead of the header
– put div placeholders inline where we want the ads to eventually render
– create a div wrapper at the bottom and after GAM has initialized, “fill” the ad slots into this wrapper and then re-append the loaded DOM objects (including each ad iframe) into the appropriate div placeholders above.
this appears to be functioning fine, but:
– i can’t honestly tell if we’ve added latency to the ads, improved parallel downloading of our stuff without much affect on the ads, or just created too much DOM overhead.
– i’ve read that DOM re-insertion of the iframes causes iframe content to reload/re-execute, which would of course be a show stopper, but it doesn’t appear to be the case.
two other possibilities:
– rather than loading iframe src dynamically, try to avoid the additional round-trip to our own servers (which adds latency) by sending the native js code via GAM (un-executed) and eval/inject it into the contentDoc of empty iframe holders (maybe initially set to src=”about:blank”)
– if iframe manipulation just can’t work, then lose the sandbox insurance and load their native js at the bottom of the page into the ad wrapper, and re-append the rendered content to the div placeholders above (still some DOM overhead, lose security, but should be faster)
any thoughts or advice? would be nice to get ad loading code to the bottom if possible…sorry for the long comment…
matt mcinvale | 05-Jun-09 at 7:53 am | Permalink |
totally off topic here, but what’s up with page speed? did you have a hand in that?
really looking forward to the next book. :)
Steve Souders | 05-Jun-09 at 10:14 am | Permalink |
@Danny – That’s a great overview of ad serving alternatives. The key question is: does it decrease ad impressions/clicks and if so, is it worth better performance? By serving ads at the top and letting them block the browser and get first dibs on connections means they’ll render faster, and probably get more impressions and more clicks. The best alternative would be to load ads asynchronously, so they load fast but don’t block other page content. At Velocity we’ll discuss these issues at the session called High Performance Ads – Is It Possible?.
@matt: A handful of awesome Googlers built Page Speed. I was not one of them. ;-)
Danny Khatib | 05-Jun-09 at 4:03 pm | Permalink |
Thanks for the pragmatic advice, Steve.
One concern I have is that many ad networks (and other third-parties) use the first js file as an entry point to download another 2-4 js files and more content. If we load their first external file near the top but asynchronously through Script DOM Element, I wonder if their subsequent js load/execution will still block other page content. If so, I suppose the middle ground would be to defer it for IE users (and others soon). I’ll try both and see what happens. Thanks again!
Péter Nagy | 05-Jun-09 at 4:13 pm | Permalink |
from: http://www.iab.net/media/file/rich_media_ajax_best_practices.pdf
A method to create the Friendly IFrame:
• Windows Operating Systems with Internet Explorer 6.0 or Higher OR Firefox 2.0 or
Higher
– Create an IFrame element and place it into the document with SRC set to a small adpage file in the
same domain as the page.
– Create a Script element and place it into the IFrame document with SRC set to the ad delivery
system
– Generate a variable called inDapIF = true in the IFrame document to identify to the ad code that it
is running inside the Dynamic IFrame.
NOTE: The IFrame should not be created using document.write as this may cause problems with AJAX pages.
The Script elements can be created using document.write as they are within the IFRAME and will not interfere with
the page content.
Péter Nagy | 05-Jun-09 at 4:16 pm | Permalink |
Sorry for the bad linebreaks… :)
• Windows Operating Systems with Internet Explorer 6.0 or Higher OR Firefox 2.0 or Higher
– Create an IFrame element and place it into the document with SRC set to a small adpage file in the same domain as the page.
– Create a Script element and place it into the IFrame document with SRC set to the ad delivery system
– Generate a variable called inDapIF = true in the IFrame document to identify to the ad code that it is running inside the Dynamic IFrame.
NOTE: The IFrame should not be created using document.write as this may cause problems with AJAX pages.
The Script elements can be created using document.write as they are within the IFRAME and will not interfere with
the page content.
Josh | 07-Jun-09 at 1:40 am | Permalink |
Great post (and series of posts you have been providing in recent months!)
If you can get Adsense and Google Analytics to implement some of these practices, soon, it will make a lot of the web a lot faster :)
Mark Rose | 02-Dec-09 at 12:02 pm | Permalink |
One of the biggest frustrations I have with ads are networks and managers (such as Google Ad Manager) that insist on using document.write to insert code. It would be trivial to write Javascript to update the HTML content of a div after onload, but it’s not done for unknown reason.
There are ways you can get around this limitation, by stuffing all the slow ad stuff at the end of your document, then using Javascript to position ads where they belong on the page. There is some talk about this technique on the Google Ad Manager forums, and it could easily be applied to any other blocking Javascript code. See http://www.google.com/support/forum/p/Google+Ad+Manager/thread?tid=7bcfaa482259cc28&hl=en
sunnybear | 06-Dec-09 at 3:58 am | Permalink |
Small remark about delayed iframes’ load. There can be possible incompatibilities with ‘raw JS’ approach (if we define SRC attribute at the bottom of the page). I.e. Google Calendar (in iframe) can’t be loaded properly. So the most safe way is to envelope the whole iframe into div, and replace contents of this div at the bottom of the page (with actual iframe).
Aaron Peters | 15-Sep-10 at 1:51 am | Permalink |
hi Steve,
Yesterday and today @pornelski and I were discussing iframes and (non-)blocking behavior on Twitter. We used your Iframes Non-Blocking example pages in the discussion.
We found out that the setTimeout trick you outline does not work in IE7 on *1st* page visit: the iframe still blocks the onload event.
We used this page for testing: https://stevesouders.com/efws/iframe-onload-nonblocking.php
Visit the page in IE7 with an empty cache. On that 1st page visit, the parent onload event is blocked. Reload page: onload is not blocked.
IE7 test results: http://www.webpagetest.org/result/100915_50CA/
IE8 test results: http://www.webpagetest.org/result/100915_50EA/
The IE8 results page clearly shows the onload is *not* blocked when visiting the page again.
I also tested on local IE7 with Pagetest plugin: same behavior.
Steve Souders | 15-Sep-10 at 1:39 pm | Permalink |
@Aaron: Yes, I believe my test has race condition issues. I recommend looking at more recent iframe async techniques esp. from Meebo.
Priya | 12-Nov-10 at 6:02 pm | Permalink |
Our website has become very slow ever since we started using iframes for headers footers. I am glad that I read this psot. Thanks Steve.
Comox Valley Homes | 08-Nov-11 at 10:18 pm | Permalink |
Great article, any test results with SCRIBD? Is there a better way to embed PDF documents that is faster?
Nenoufar | 03-May-12 at 8:41 am | Permalink |
Now that I’ve read this article, I really think iframes are evil! I’m going to have to replace the 2 iframes i have on my site for facebook and use their xfbml version instead to speed my my site.
Travis J | 30-Nov-12 at 4:59 pm | Permalink |
Side note, if you are getting blocked from the iframes loading while your page loads, considering loading the whole iframe from script from a setTimeout called in window.onload.
Joseph Ishak | 19-Mar-13 at 12:58 pm | Permalink |
check this technique I built in the link here:
http://async-iframe.blogspot.com/2013/03/async-iframe-technique.html