512

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.

robsch
  • 8,466
  • 8
  • 56
  • 87
larssg
  • 5,313
  • 3
  • 18
  • 21

23 Answers23

591

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 its 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 its 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 its onload fires, it evaluates its 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'></iframe>

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+'&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>
Tomas
  • 52,167
  • 46
  • 207
  • 345
ConroyP
  • 38,904
  • 16
  • 76
  • 86
  • 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 Jan 18 '09 at 21:45
  • 9
    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 Aug 31 '09 at 16:50
  • 5
    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 May 21 '10 at 20:51
  • 1
    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 Jun 01 '10 at 21:14
  • If framed (bar.net, the first iframe) is **https** can it still access home(the main page) via parent.parent.resizeIframe(height); ? – Keyo Jul 06 '10 at 00:50
  • 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 Jul 20 '10 at 06:36
  • 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 :/ – Petrogad Sep 01 '10 at 13:47
  • 9
    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 Sep 09 '10 at 02:21
  • 1
    @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 `` ... just thinking out loud. – Chris Jacob Sep 09 '10 at 02:28
  • suggest `` ... in IE8 the iframe was visible in the page layout. – Chris Jacob Sep 09 '10 at 03:04
  • Suggest: ``. 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 to get property Window.resizeIframe from .[Break on this error] parent.parent.resizeIframe(height);` – Chris Jacob Sep 09 '10 at 03:48
  • 1
    Also, this doesn't work if you have dynamic changing content on pages. – Petrogad Sep 09 '10 at 18:47
  • Can someone please explain what it means that Home can communicate with Framed, but Framed can't communicate with Home. Is it a matter of who can starts the communication process? – Dan Roberts Jul 27 '11 at 19:17
  • Does this still work? This looks like an unintentional feature (that may be "fixed" in an automatic browser update.) – finnw Sep 26 '11 at 17:59
  • 2
    @Frederico, This approach does does still work, but the solution needs to be revised to work in the most recent versions of FF and Chrome. The scroll height will improperly use the iframe height if it is larger than page being rendered. This causes the iframe to potentially increase 60px with every page turn. The needed fix is to have an ID'd element inside the framed HTML. Get it's scroll height instead of the document and it works like a champ. – Thomas Nov 16 '11 at 20:55
  • 1
    Just like @Frederico said, in new version Firefox, the height keep adding previous size, however you can fix this by not adding additional height in this code: `document.getElementById('frame_name_here').height = parseInt(height)+60;`. Just use the given height and it will fix the problem, but if you want some space in your content, just add some padding on framed.html body, i think that should be fix the problem – adods Mar 14 '12 at 10:03
  • Are there any security implications in receiving the URL of framehelper.html as a query string parameter? – Alex J Sep 30 '12 at 18:29
  • 1
    Thanks for the great explanation! Another possible improvement to the script is to select all the `img` tags via jQuery and bind to their 'load' events so that you re-update the iFrame height every time an image successfully loads or else trigger the iFrame resize only once all images has been loaded. This is since the content height might change when images are loaded due to the actual image sizes, text wrapping etc.. – Mark Cassar Mar 21 '13 at 09:50
  • There was a small mistake in the tree, I have fixed it. Please leave comment or discuss the change if I was mistaken, but I think now it is as you meant it. – Tomas Oct 30 '13 at 17:35
  • 1
    Referring to your table - you say that **helper can communicate with framed? I would say it can't because of the same reason framed can't communicate with home.** Isn't it a mistake? – Tomas Jan 18 '14 at 11:59
  • @ChrisJacob Good call on setting the initial height. I set a `min-height` in CSS instead. – andrewtweber Mar 26 '14 at 17:46
  • This is a really out of date answer, for better options see http://stackoverflow.com/questions/14334154/iframe-auto-adjust-height-as-content-changes – David Bradshaw Jun 09 '14 at 23:23
  • Thanks a lot. Thank you so much for this answer :). Between, I added this `function resizeIframe(_height) {$("#frame_name_here").height(_height) }` in my www.foo.com website for set height of iframe – Selvamani Jun 11 '14 at 18:14
  • Good post, but anyway again, how to catch child iframe resize? The only thing you need auto height often is to prevent resized iframe scrollbars. – Rantiev Sep 12 '14 at 10:20
  • I'm in the same boat...supply framed content to other domains. Based on your solution, are you saying that you have to frame a helper.html file from every domain that wants to add your content? – Michael Yaeger May 07 '16 at 04:28
  • 2
    Unfortunately this no longer seems to work. I carefully implemented this but receive the following javascript error in Chrome console: Uncaught DOMException: Blocked a frame with origin "http://www.example.com" from accessing a cross-origin frame. – Stefan Feb 14 '17 at 14:20
  • 2017 update: in Chrome I am able to call window.parent.postMessage(data, '*'); and the parent frame is able to receive the message. It works cross-origin. – Alexander Taylor Dec 20 '17 at 00:49
  • This hugely up-voted answer is unnecessarily complex, and isn't right. Look, if you have a car with wheels of square shape, with enough power, you can drive that car, but that doesn't mean that's right. The right approach is using postMessage (inter-document messaging, supported by all modern browsers). See this answer: https://stackoverflow.com/a/3219970/11729048 by Chris Jacob. Also, I provided a real working example here: https://stackoverflow.com/a/67044630/11729048 – latitov Apr 11 '21 at 11:52
80

If you do not need to handle iframe content from a different domain, 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>
Adam Lynch
  • 3,250
  • 5
  • 31
  • 62
Ahmy
  • 5,038
  • 7
  • 37
  • 50
  • 9
    It may be more optimal to pass the element itself to the function to avoid all those `document.getElementById(id)`s you have and simplify the code. – Muhd Oct 13 '11 at 21:02
  • 1
    This does not work for me as the contentWindow.document.body.scrollHeight holds 0 and not the real height. – kroiz Nov 01 '11 at 14:38
  • 4
    This solution, as all the other ones I tried, works only if the height increases, if from a tall page you navigate to a shorter one, the height is still set according to the height of the first one. Did anyone find a solution for this problem? – Eugenio Dec 31 '12 at 14:41
  • 6
    @Eugenio and everyone else including me, to fix this final problem: see http://stackoverflow.com/questions/3053072/iframe-resizing-with-scrollheight-in-chrome-safari Before you ask for the height of the document inside the iframe you should set the height of the iframe object to "auto". Note that you should use style.height not just height as in the answer above – Kyle Jan 09 '13 at 21:41
  • @Kyle it works, thanks! There is still a residual problem with FF: the height calculated is always a few pixels less than the needed one, so you always see the vertical scrollbar...adding +something doesn't fix the problem for me. – Eugenio Jan 10 '13 at 16:48
  • 7
    I don't get it... you check for the existence of document.getElementById and then call it outside the checked area anyway? – Legolas Feb 06 '13 at 16:45
  • It works for me! I'm embedding another script that displays user generated html from the same domain, it doesn't mess up my styles. – MikeGA Jun 26 '13 at 00:42
  • Also note, this doesn't work for iframe with 'sandbox' attribute – seriyPS Mar 01 '14 at 23:55
  • the question is for cross domain, but your answer specifically starts: "If you do not need to handle iframe content from a different domain". so it's as @kevin wrote, and (along with the current officially selected answer from ConroyP) cannot be considered the full answer - which, it seems, MAY exist (see other answers below). – pashute Feb 19 '18 at 07:04
42

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.)

The iFrame-Resizer library uses postMessage to keep an iFrame sized to it's content, along with MutationObserver to detect changes to the content and doesn't depend on jQuery.

https://github.com/davidjbradshaw/iframe-resizer

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/

David Bradshaw
  • 10,116
  • 3
  • 33
  • 61
Chris Jacob
  • 10,898
  • 7
  • 45
  • 42
  • postMessage on its own isn't cross-browser, but the jQuery plugin seems to do a good job faking it when necessary. Only real problem is the "junk" that gets added to the URL bar in the cases where postMessage isn't supported. – Herms Aug 01 '11 at 15:50
  • 2
    postMessage seems to be X-Browser. Only the IE has to bugs or things to care: 1. Communication only between frames or iframes 2. Only capable of posting strings. See: http://caniuse.com/x-doc-messaging – Christian Kuetbach Jan 23 '13 at 18:49
  • 3
    Would have been nice to see you answer the question instead of explaining about an assistive technology that helps with a solution.. – hitautodestruct Jul 13 '14 at 15:21
  • periscopedata is using postMessage, if it is good enough for them, it is good enough for us. Here are their docs: https://doc.periscopedata.com/docv2/embed-api-options – Yevgeniy Afanasyev Jul 26 '17 at 01:03
8

The simplest way using jQuery:

$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
    $(this).css("height", $(this).contents().height() + "px");
});
Zoltan Toth
  • 45,022
  • 11
  • 108
  • 129
Omer Arshad
  • 485
  • 6
  • 14
5

Finally I found some other solution for sending data to parent website from iframe using window.postMessage(message, targetOrigin);. Here I explain How I did.

Site A = http://foo.com Site B = http://bar.com

SiteB is loading inside the siteA website

SiteB website have this line

window.parent.postMessage("Hello From IFrame", "*"); 

or

window.parent.postMessage("Hello From IFrame", "http://foo.com");

Then siteA have this following code

// Here "addEventListener" is for standards-compliant web browsers and "attachEvent" is for IE Browsers.
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];


var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

// Listen to message from child IFrame window
eventer(messageEvent, function (e) {
   alert(e.data);
   // Do whatever you want to do with the data got from IFrame in Parent form.
}, false); 

If you want to add security connection you can use this if condition in eventer(messageEvent, function (e) {})

if (e.origin == 'http://iframe.example.com') {
    alert(e.data); 
    // Do whatever you want to do with the data got from IFrame in Parent form.
}

For IE

Inside IFrame:

 window.parent.postMessage('{"key":"value"}','*');

Outside:

 eventer(messageEvent, function (e) {
   var data = jQuery.parseJSON(e.data);
   doSomething(data.key);
 }, false);
Selvamani
  • 6,590
  • 3
  • 27
  • 41
5

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.

robsch
  • 8,466
  • 8
  • 56
  • 87
Macho Matt
  • 1,169
  • 4
  • 14
  • 32
4

may be a bit late, as all the other answers are older :-) but... here´s my solution. Tested in actual FF, Chrome and Safari 5.0.

css:

iframe {border:0; overflow:hidden;}

javascript:

$(document).ready(function(){
    $("iframe").load( function () {
        var c = (this.contentWindow || this.contentDocument);
        if (c.document) d = c.document;
        var ih = $(d).outerHeight();
        var iw = $(d).outerWidth();
        $(this).css({
            height: ih,
            width: iw
        });
    });
});

Hope this will help anybody.

ddlab
  • 854
  • 12
  • 26
  • @pashute - do you mean "cross domain"? – Luke Feb 17 '18 at 05:22
  • sorry. Meant: Doesn't work cross platform... Here's a public gist: https://gist.github.com/pashute/c4705ce7f767f50fdf56d0030ecf9192 Gets a Refused to execute error. Changing the script to have type="text/javascript" doesn't help. Nor setting the iframe width and height (say 80%). – pashute Feb 19 '18 at 07:53
3

This answer is only applicable for websites which uses Bootstrap. The responsive embed feature of the Bootstrap does the job. It is based on the width (not height) of the content.

<!-- 16:9 aspect ratio -->
<div class="embed-responsive embed-responsive-16by9">
  <iframe class="embed-responsive-item" src="http://www.youtube.com/embed/WsFWhL4Y84Y"></iframe>
</div>

jsfiddle: http://jsfiddle.net/00qggsjj/2/

http://getbootstrap.com/components/#responsive-embed

Razan Paul
  • 12,148
  • 3
  • 64
  • 59
  • This works for embeding videos but if i want to embed a web page with a height it gets scrolling and a weird height. – cabaji99 Sep 29 '16 at 15:20
2

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&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.

  • The link is down. Could you please share the relevant code in your answer so that it remains relevant to this thread? – zed Mar 11 '19 at 19:26
1
<html>
<head>
<script>
function frameSize(id){
var frameHeight;

document.getElementById(id).height=0 + "px";
if(document.getElementById){
    newheight=document.getElementById(id).contentWindow.document.body.scrollHeight;    
}

document.getElementById(id).height= (frameHeight) + "px";
}
</script>
</head>

<body>

<iframe id="frame"  src="startframe.html" frameborder="0" marginheight="0" hspace=20     width="100%" 

onload="javascript:frameSize('frame');">

<p>This will work, but you need to host it on an http server, you can do it locally.    </p>
</body>
</html>
webd3sign
  • 21
  • 2
1

I'm implementing ConroyP's frame-in-frame solution to replace a solution based on setting document.domain, but found it to be quite hard determining the height of the iframe's content correctly in different browsers (testing with FF11, Ch17 and IE9 right now).

ConroyP uses:

var height = document.body.scrollHeight;

But that only works on the initial page load. My iframe has dynamic content and I need to resize the iframe on certain events.

What I ended up doing was using different JS properties for the different browsers.

function getDim () {
    var body = document.body,
        html = document.documentElement;

    var bc = body.clientHeight;
    var bo = body.offsetHeight;
    var bs = body.scrollHeight;
    var hc = html.clientHeight;
    var ho = html.offsetHeight;
    var hs = html.scrollHeight;

    var h = Math.max(bc, bo, bs, hc, hs, ho);

    var bd = getBrowserData();

    // Select height property to use depending on browser
    if (bd.isGecko) {
        // FF 11
        h = hc;
    } else if (bd.isChrome) {
        // CH 17
        h = hc;
    } else if (bd.isIE) {
        // IE 9
        h = bs;
    }

    return h;
}

getBrowserData() is browser detect function "inspired" by Ext Core's http://docs.sencha.com/core/source/Ext.html#method-Ext-apply

That worked well for FF and IE but then there were issues with Chrome. One of the was a timing issue, apparently it takes Chrome a while to set/detect the hight of the iframe. And then Chrome also never returned the height of the content in the iframe correctly if the iframe was higher than the content. This wouldn't work with dynamic content when the height is reduced.

To solve this I always set the iframe to a low height before detecting the content's height and then setting the iframe height to it's correct value.

function resize () {
    // Reset the iframes height to a low value.
    // Otherwise Chrome won't detect the content height of the iframe.
    setIframeHeight(150);

    // Delay getting the dimensions because Chrome needs
    // a few moments to get the correct height.
    setTimeout("getDimAndResize()", 100);
}

The code is not optimized, it's from my devel testing :)

Hope someone finds this helpful!

0

Here's a jQuery approach that adds the info in json via the src attribute of the iframe. Here's a demo, resize and scroll this window.. the resulting url with json looks like this... http://fiddle.jshell.net/zippyskippy/RJN3G/show/#{docHeight:5124,windowHeight:1019,scrollHeight:571}#

Here's the source code fiddle http://jsfiddle.net/zippyskippy/RJN3G/

function updateLocation(){

    var loc = window.location.href;
    window.location.href = loc.replace(/#{.*}#/,"") 
        + "#{docHeight:"+$(document).height() 
        + ",windowHeight:"+$(window).height()
        + ",scrollHeight:"+$(window).scrollTop()
        +"}#";

};

//setInterval(updateLocation,500);

$(window).resize(updateLocation);
$(window).scroll(updateLocation);
chad steele
  • 680
  • 7
  • 11
0

get iframe content height then give it to this iframe

 var iframes = document.getElementsByTagName("iframe");
 for(var i = 0, len = iframes.length; i<len; i++){
      window.frames[i].onload = function(_i){
           return function(){
                     iframes[_i].style.height = window.frames[_i].document.body.scrollHeight + "px";
                     }
      }(i);
 }
0

Work with jquery on load (cross browser):

 <iframe src="your_url" marginwidth="0"  marginheight="0" scrolling="No" frameborder="0"  hspace="0" vspace="0" id="containiframe" onload="loaderIframe();" height="100%"  width="100%"></iframe>

function loaderIframe(){
var heightIframe = $('#containiframe').contents().find('body').height();
$('#frame').css("height", heightFrame);
 }  

on resize in responsive page:

$(window).resize(function(){
if($('#containiframe').length !== 0) {
var heightIframe = $('#containiframe').contents().find('body').height();
 $('#frame').css("height", heightFrame);
}
});
0

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
  • 10,770
  • 2
  • 33
  • 49
0

When you want to zoom out a web page to fit it into the iframe size:

  1. You should resize the iframe to fit it with the content
  2. Then you should zoom out the whole iframe with the loaded web page content

Here is an example:

<div id="wrap">
   <IFRAME ID="frame" name="Main" src ="http://www.google.com" />
</div>

<style type="text/css">
    #wrap { width: 130px; height: 130px; padding: 0; overflow: hidden; }
    #frame { width: 900px; height: 600px; border: 1px solid black; }
    #frame { zoom:0.15; -moz-transform:scale(0.15);-moz-transform-origin: 0 0; }
</style>
Brian Mains
  • 49,697
  • 35
  • 139
  • 249
kaw
  • 1
0

This is an old thread, but in 2020 it's still a relevant question. I've actually posted this answer in another old thread as well^^ (https://stackoverflow.com/a/64110252/4383587)


Just wanted to share my solution and excitement. It took me four entire days of intensive research and failure, but I think I've found a neat way of making iframes entirely responsive! Yey!

I tried a ton of different approaches... I didn't want to use a two-way communication tunnel as with postMessage because it's awkward for same-origin and complicated for cross-origin (as no admin wants to open doors and implement this on your behalf).

I've tried using MutationObservers and still needed several EventListeners (resize, click,..) to ensure that every change of the layout was handled correctly. - What if a script toggles the visibility of an element? Or what if it dynamically preloads more content on demand? - Another issue was getting an accurate height of the iframe contents from somewhere. Most people suggest using scrollHeight or offsetHeight, or combination of it by using Math.max. The problem is, that these values don't get updated until the iframe element changes its dimensions. To achieve that you could simply reset the iframe.height = 0 before grabbing the scrollHeight, but there are even more caveats to this. So, screw this.

Then, I had another idea to experiment with requestAnimationFrame to get rid of my events and observers hell. Now, I could react to every layout change immediately, but I still had no reliable source to infer the content height of the iframe from. And theeen I discovered getComputedStyle, by accident! This was an enlightenment! Everything just clicked.

Well, see the code I could eventually distill from my countless attempts.

function fit() {
    var iframes = document.querySelectorAll("iframe.gh-fit")

    for(var id = 0; id < iframes.length; id++) {
        var win = iframes[id].contentWindow
        var doc = win.document
        var html = doc.documentElement
        var body = doc.body
        var ifrm = iframes[id] // or win.frameElement

        if(body) {
            body.style.overflowX = "scroll" // scrollbar-jitter fix
            body.style.overflowY = "hidden"
        }
        if(html) {
            html.style.overflowX = "scroll" // scrollbar-jitter fix
            html.style.overflowY = "hidden"
            var style = win.getComputedStyle(html)
            ifrm.width = parseInt(style.getPropertyValue("width")) // round value
            ifrm.height = parseInt(style.getPropertyValue("height"))
        }
    }

    requestAnimationFrame(fit)
}

addEventListener("load", requestAnimationFrame.bind(this, fit))

That is it, yes! - In your HTML code write <iframe src="page.html" class="gh-fit gh-fullwidth"></iframe>. The gh-fit is a just fake CSS class, used to identify which iframe elements in your DOM should be affect by the script. The gh-fullwidth is a simple CSS class with one rule width: 100%;.

The above script automatically fetches all iframes from the DOM, that have a .gh-fit class assigned. It then grabs and uses the pre-calculated style values for width and height from document.getComputedStyle(iframe), which always contain a pixel-perfect size of that element!!! Just perfect!

Note, this solution doesn't work cross-origin (nor does any other solution, without a two-way communication strategy like IFrameResizer). JS simply can't access the DOM of an iframe, if it doesn't belong to you.

The only other cross-origin solution I can think of, is to use a proxy like https://github.com/gnuns/allorigins. But this would involve deep-copying every request you make - in other words - you 'steal' the entire page source code (to make it yours and let JS access the DOM) and you patch every link/path in this source, so that it goes through the proxy as well. The re-linking routine is a tough one, but doable.

I'll probably try myself at this cross-origin problem, but that's for another day. Enjoy the code! :)

herrsch
  • 150
  • 1
  • 8
0

couldn't find something that perfectly handled large texts + large images, but i ended up with this, seems this gets it right, or nearly right, every single time:

    iframe.addEventListener("load",function(){
        // inlineSize, length, perspectiveOrigin, width
        let heightMax = 0;
        // this seems to work best with images...
        heightMax = Math.max(heightMax,iframe.contentWindow.getComputedStyle(iframe.contentWindow.document.body).perspectiveOrigin.split("px")[0]);
        // this seems to work best with text...
        heightMax = Math.max(heightMax,iframe.contentWindow.document.body.scrollHeight);
        // some large 1920x1080 images always gets a little bit off on firefox =/
        const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
        if(isFirefox && heightMax >= 900){
            // grrr..
            heightMax = heightMax + 100;
        }

        iframe.style.height = heightMax+"px";
        //console.log(heightMax);
    });

hanshenrik
  • 15,263
  • 3
  • 28
  • 61
0

David Bradshaw and Chris Jacob already suggested using the postMessage approach. And I totally agree, that the proper way of doing things like these.

I just want to post an example, real code that works, in case it'll be a ready answers for some.

On the iframed-side:

<body onload="docResizePipe()">
<script>
var v = 0;
const docResizeObserver = new ResizeObserver(() => {
    docResizePipe();
});
docResizeObserver.observe(document.querySelector("body"));
function docResizePipe() {
    v += 1;
    if (v > 5) {
        return;
    }
    var w = document.body.scrollWidth;
    var h = document.body.scrollHeight;
    window.parent.postMessage([w,h], "*");
}
setInterval(function() {
    v -= 1;
    if (v < 0) {
        v = 0;
    }
}, 300);
</script>

Note the recursion-blocking mechanics - it was necessary because of apparently a bug in Firefox, but anyways let it be there.

On the parent document side:

<iframe id="rpa-frame" src="3.html" style="border: none;"></iframe>
<script>
var rpaFrame = document.getElementById("rpa-frame");

window.addEventListener("message", (event) => {
    var width = event.data[0];
    var height = event.data[1];
    rpaFrame.width = parseInt(width)+60;
    rpaFrame.height = parseInt(height)+60;
    console.log(event);
}, false);
</script>

Hope it'll be useful.

latitov
  • 302
  • 1
  • 7
-1

Using jQuery:

parent.html

<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<style>
iframe {
    width: 100%;
    border: 1px solid black;
}
</style>
<script>
function foo(w, h) {
    $("iframe").css({width: w, height: h});
    return true;  // for debug purposes
}
</script>
<iframe src="child.html"></iframe>
</body>

child.html

<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function() {
    var w = $("#container").css("width");
    var h = $("#container").css("height");

    var req = parent.foo(w, h);
    console.log(req); // for debug purposes
});
</script>
<style>
body, html {
    margin: 0;
}
#container {
    width: 500px;
    height: 500px;
    background-color: red;
}
</style>
<div id="container"></div>
</body>
The Onin
  • 3,947
  • 2
  • 30
  • 45
-2

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
  • 27,986
  • 15
  • 77
  • 101
-2

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
  • 64,767
  • 17
  • 135
  • 193
  • 3
    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 Sep 30 '08 at 14:23
-4

I have an easy solution and requires you to determine the width and height in the link, please try (It works with most browsers):

<a href='#' onClick=" document.getElementById('myform').src='t2.htm';document.getElementById('myform').width='500px'; document.getElementById('myform').height='400px'; return false">500x400</a>