views:

22503

answers:

10

I am working on an iGoogle-like application. Content from other applications (on other domains) is shown using iframes.

How do I resize the iframes to fit the height of the iframes' content?

I've tried to decipher the javascript Google uses but it's obfuscated, and searching the web has been fruitless so far.

Update: Please note that content is loaded from other domains, so the same-origin policy applies.

A: 

TinyMCE does this and it is not obfuscated.

Nick Berardi
You're being generous ;)
eyelidlessness
A: 

This is slightly tricky as you have to know when the iframe page has loaded, which is difficuly when you're not in control of its content. Its possible to add an onload handler to the iframe, but I've tried this in the past and it has vastly different behaviour across browsers (not guess who's the most annoying...). You'd probably have to add a function to the iframe page that performs the resize and inject some script into the content that either listens to load events or resize events, which then calls the previous function. I'm thinking add a function to the page since you want to make sure its secure, but I have no idea how easy it will be to do.

roryf
A: 

Something on the lines of this i belive should work.

parent.document.getElementById(iFrameID).style.height=framedPage.scrollHeight;

Load this with your body onload on the iframe content.

Ólafur Waage
That is not possible as the content in the iFrames is loaded from other domains, so trying to do that results in an error, such as this from Firefox: "Permission denied to get property Window.document".
larssg
+1  A: 

The solution on http://www.phinesolutions.com/use-jquery-to-adjust-the-iframe-height.html works great (uses jQuery):

<script type=”text/javascript”>
$(document).ready(function() {
var theFrame = $(”#iFrameToAdjust”, parent.document.body);
theFrame.height($(document.body).height() + 30);
});
</script>

I don't know that you need to add 30 to the length... 1 worked for me.

FYI: If you already have a "height" attribute on your iFrame, this just adds style="height: xxx". This might not be what you want.

Macho Matt
But does it work cross-domain?
Bruce Alderman
Not cross-domain, no.
Volomike
A: 

iGoogle gadgets have to actively implement resizing, so my guess is in a cross-domain model you can't do this without the remote content taking part in some way. If your content can send a message with the new size to the container page using typical cross-domain communication techniques, then the rest is simple.

Joeri Sebrechts
+72  A: 

We had this type of problem, but slightly in reverse to your situation - we were providing the iframed content to sites on other domains, so the same origin policy was also an issue. After many hours spent trawling google, we eventually found a (somewhat..) workable solution, which you may be able to adapt to your needs.

There is a way around the same origin policy, but it requires changes on both the iframed content and the framing page, so if you haven't the ability to request changes on both sides, this method won't be very useful to you, i'm afraid.

There's a browser quirk which allows us to skirt the same origin policy - javascript can communicate either with pages on it's own domain, or with pages it has iframed, but never pages in which it is framed, e.g. if you have:

 www.foo.com/home.html, which iframes
 |-> www.bar.net/framed.html, which iframes
 |----> www.foo.com/helper.html

then home.html can communicate with framed.html (iframed) and helper.html (same domain).

 Communication options for each page:
 +-------------------------+-----------+-------------+-------------+
 |                         | home.html | framed.html | helper.html |
 +-------------------------+-----------+-------------+-------------+
 | www.foo.com/home.html   |    N/A    |     YES     |     YES     |
 | www.bar.net/framed.html |    NO     |     N/A     |     YES     |
 | www.foo.com/helper.html |    YES    |     YES     |     N/A     |
 +-------------------------+-----------+-------------+-------------+

framed.html can send messages to helper.html (iframed) but not home.html (child can't communicate cross-domain with parent).

The key here is that helper.html can receive messages from framed.html, and can also communicate with home.html.

So essentially, when framed.html loads, it works out it's own height, tells helper.html, which passes the message on to home.html, which can then resize the iframe in which framed.html sits.

The simplest way we found to pass messages from framed.html to helper.html was through a URL argument. To do this, framed.html has an iframe with src='' specified. When it's onload fires, it evaluates it's own height, and sets the src of the iframe at this point to helper.html?height=N

There's an explanation here of how facebook handle it, which may be slightly clearer than mine above!


Code

In www.foo.com/home.html, the following javascript code is required (this can be loaded from a .js file on any domain, incidentally..):

<script>
  // Resize iframe to full height
  function resizeIframe(height)
  {
    // "+60" is a general rule of thumb to allow for differences in
    // IE & and FF height reporting, can be adjusted as required..
    document.getElementById('frame_name_here').height = parseInt(height)+60;
  }
</script>
<iframe id='frame_name_here' src='http://www.bar.net/framed.html'&gt;&lt;/iframe&gt;

In www.bar.net/framed.html:

<body onload="iframeResizePipe()">
<iframe id="helpframe" src='' height='0' width='0' frameborder='0'></iframe>

<script type="text/javascript">
  function iframeResizePipe()
  {
     // What's the page height?
     var height = document.body.scrollHeight;

     // Going to 'pipe' the data to the parent through the helpframe..
     var pipe = document.getElementById('helpframe');

     // Cachebuster a precaution here to stop browser caching interfering
     pipe.src = 'http://www.foo.com/helper.html?height='+height+'&amp;cacheb='+Math.random();

  }
</script>

Contents of www.foo.com/helper.html:

<html> 
<!-- 
This page is on the same domain as the parent, so can
communicate with it to order the iframe window resizing
to fit the content 
--> 
  <body onload="parentIframeResize()"> 
    <script> 
      // Tell the parent iframe what height the iframe needs to be
      function parentIframeResize()
      {
         var height = getParam('height');
         // This works as our parent's parent is on our domain..
         parent.parent.resizeIframe(height);
      }

      // Helper function, parse param from request string
      function getParam( name )
      {
        name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
        var regexS = "[\\?&]"+name+"=([^&#]*)";
        var regex = new RegExp( regexS );
        var results = regex.exec( window.location.href );
        if( results == null )
          return "";
        else
          return results[1];
      }
    </script> 
  </body> 
</html>
ConroyP
+1 for the detailed explanation of WHY this works
Bruce Alderman
trully full of insight. I was wondering why igoogle.com had a helper.html file! Thanks
NTulip
Just one thing! When the swf goes back to it's normal size, how are you going to resize the iframe? Do you have the iframeResizePipe on a timer or is it called again by the swf? In my company when have call to js in the swf so we know exactly when it changes size
AntonioCS
Awesome, thanks! I made a couple additions:Use jQuery to get the body height in framed.html (FF issue, body kept growing):var height = $(document.body).height();Used a body onload event to create the frame in home.html similar to your approach in framed.html. Addresses not updating the frame on refresh in FF and Safari.
Abdullah Jibaly
Genius! In addition to Abdullah's additions: rather than setting the body onload to call `parentIframeResize()`, I used JQuery to catch both the page load and any resizing events: `$(document).ready(iframeResizePipe); $(window).resize(iframeResizePipe);` This allows me to set the iframe `width="100%"` and if the window width changes to make text wrap or something, the inner frame will recognize that it needs to be resized.
StriplingWarrior
So this is a solution only if you can convince the site owners to change code in their page. If you can't do that, then this doesn't work.
Volomike
If framed (bar.net, the first iframe) is **https** can it still access home(the main page) via parent.parent.resizeIframe(height); ?
Keyo
The parent.parent.resizeIFrame() call fails in Opera. Error message: "A page on the public Internet requests data from your private intranet. For security reasons, automatic access is blocked, but you may choose to continue.".... any way to access the parent's parent without invoking this error?
frankadelic
Following this with the new Firefox Version, along with Chrome, Safari and IE.. when going back and forth between pages, the window continues to be resized and keeps adding to the previous size. This appears to no longer work :/
Frederico
I suggest giving the iframe id=frame_name_here a default height attribute such as height="2000". This means when the page loads the framed content is visible. When the resize happens there is no "flicker". The resize is shrinking/expanding below the page fold. If you know the maximum height of your iframed content then use that value... this also results in a better noscript experience.
Chris Jacob
@StriplingWarrior I would imagine (and correct me if I'm wrong) but wouldn't `$(document).ready(iframeResizePipe);` call the resize too early?... i.e. before images had been retrieved and effected the body height? Might be ok if you specify height on all your images `<img height="value" />` ... just thinking out loud.
Chris Jacob
suggest `<iframe id="helpframe" style="display:none;"></iframe>` ... in IE8 the iframe was visible in the page layout.
Chris Jacob
Suggest: `<iframe id='frame_name_here' name='frame_name_here' src='http://www.bar.net/framed.html'></iframe>`. Then `function iframeResizePipe(){if(window.name === 'frame_name_here'){ .. do the resize only if the page is contained inside the iframe .. }}`. This avoids getting message in Firebug that say `Permission denied for <http://www.foo.com> to get property Window.resizeIframe from <http://localhost>.[Break on this error] parent.parent.resizeIframe(height);`
Chris Jacob
Also, this doesn't work if you have dynamic changing content on pages.
Frederico
+5  A: 

Try this code it will solve the problem completely and it's simple:

<script language="JavaScript">
<!--
function autoResize(id){
    var newheight;
    var newwidth;

    if(document.getElementById){
        newheight=document.getElementById(id).contentWindow.document .body.scrollHeight;
        newwidth=document.getElementById(id).contentWindow.document .body.scrollWidth;
    }

    document.getElementById(id).height= (newheight) + "px";
    document.getElementById(id).width= (newwidth) + "px";
}
//-->
</script>

<IFRAME SRC="usagelogs/default.aspx" width="100%" height="200px" id="iframe1" marginheight="0" frameborder="0" onLoad="autoResize('iframe1');"></iframe>
Ahmy
Nice script but not cross-domain.
Sohnee
+1 from me, I don't need a cross-domain solution and this works a treat from me :) Thanks pal
Binary Worrier
Doesn't work cross-domain.
Volomike
A: 

Here is a simple solution using a dynamically generated style sheet served up by the same server as the iframe content. Quite simply the style sheet "knows" what is in the iframe, and knows the dimensions to use to style the iframe. This gets around the same origin policy restrictions.

http://www.8degrees.co.nz/2010/06/09/dynamically-resize-an-iframe-depending-on-its-content/

So the supplied iframe code would have an accompanying style sheet like so...

<link href="http://your.site/path/to/css?contents_id=1234&amp;dom_id=iframe_widget" rel="stylesheet" type="text/css" />
 <iframe id="iframe_widget" src="http://your.site/path/to/content?content_id=1234" frameborder="0" width="100%" scrolling="no"></iframe>

This does require the server side logic being able to calculate the dimensions of the rendered content of the iframe.

Vaughan Rowsell
A: 

This worked almost for me....But the end result was to dynmically generate the IFRAME.

I have put my Entire Code for the Javascript into this page..

http://douglubey.com/Dynamically_Generate_InlineFrame_UsingJavscript_WithBrowser_Height_And_Width.aspx

2010-06-16

SUMMARY: How to dynamically generate an inline frame using Javascript. so that the dimensions of the inline frame match the browser windows dimensions. I had to create this structure for my SCORM javascript architecture. I did not want to open a new window for the SCORM CONTENT (SCO) when it plays. I wanted it to be in the same browser window.

Most of the Learning Management System (LMS) courses use flash (built with CAPTIVATE 4). Flash will allow you to resize the screen and the Flash object will reesize with it in most browsers. Well Firefox does not allow the resize to occur on the Flash object. So I before I load the flash object, I need to get the "HIGHEST" resolution possible before I insert the flash object.

Thanks for your help: Doug Lubey of Louisiana

Doug Lubey
A: 

https://developer.mozilla.org/en/DOM/window.postMessage

window.postMessage()

window.postMessage is a method for safely enabling cross-origin communication. Normally, scripts on different pages are only allowed to access each other if and only if the pages which executed them are at locations with the same protocol (usually both http), port number (80 being the default for http), and host (modulo document.domain being set by both pages to the same value). window.postMessage provides a controlled mechanism to circumvent this restriction in a way which is secure when properly used.

Summary

window.postMessage, when called, causes a MessageEvent to be dispatched at the target window when any pending script that must be executed completes (e.g. remaining event handlers if window.postMessage is called from an event handler, previously-set pending timeouts, etc.). The MessageEvent has the type message, a data property which is set to the string value of the first argument provided to window.postMessage, an origin property corresponding to the origin of the main document in the window calling window.postMessage at the time window.postMessage was called, and a source property which is the window from which window.postMessage is called. (Other standard properties of events are present with their expected values.)

jQuery: Cross-domain scripting goodness

http://benalman.com/projects/jquery-postmessage-plugin/

Has demo of resizing iframe window...

http://benalman.com/code/projects/jquery-postmessage/examples/iframe/

This article shows how to remove the dependency on jQuery... Plus has a lot of useful info and links to other solutions.

http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/

Barebones example...

http://onlineaspect.com/uploads/postmessage/parent.html

HTML 5 working draft on window.postMessage

http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages

John Resig on Cross-Window Messaging

http://ejohn.org/blog/cross-window-messaging/

Chris Jacob