Resize iFrame View Based on Content [duplicate] - javascript

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.

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>

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>

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/

The simplest way using jQuery:
$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
$(this).css("height", $(this).contents().height() + "px");
});

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

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.

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.

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

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.

If you have control over the iframe content , I strongly recommend using
ResizeObserver
Just insert the following at the end of srcdoc attribute of iframe ,
escape it if needed.
<script type="text/javascript">
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
// console.log(window.frameElement);
window.frameElement.style.height =cr.height +30+ "px";
}
});
ro.observe(document.body);
</script>

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!

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

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! :)

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.

When you want to zoom out a web page to fit it into the iframe size:
You should resize the iframe to fit it with the content
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>

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

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);
}

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);
}
});

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.

I have been reading a lot of the answers here but nearly everyone gave some sort of cross-origin frame block.
Example error:
Uncaught DOMException: Blocked a frame with origin "null" from
accessing a cross-origin frame.
The same for the answers in a related thread:
Make iframe automatically adjust height according to the contents without using scrollbar?
I do not want to use a third party library like iFrame Resizer or similar library either.
The answer from #ChrisJacob is close but I'm missing a complete working example and not only links. #Selvamani and #latitov are good complements as well.
https://stackoverflow.com/a/3219970/3850405
I'm using width="100%" for the iframe but the code can be modified to work with width as well.
This is how I solved setting a custom height for the iframe:
Embedded iframe:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description"
content="Web site" />
<title>Test with embedded iframe</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<iframe id="ifrm" src="https://localhost:44335/package/details?key=123" width="100%"></iframe>
<script type="text/javascript">
window.addEventListener('message', receiveMessage, false);
function receiveMessage(evt) {
console.log("Got message: " + JSON.stringify(evt.data) + " from origin: " + evt.origin);
// Do we trust the sender of this message?
if (evt.origin !== "https://localhost:44335") {
return;
}
if (evt.data.type === "frame-resized") {
document.getElementById("ifrm").style.height = evt.data.value + "px";
}
}
</script>
</body>
</html>
iframe source, example from Create React App but only HTML and JS is used.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description"
content="Web site created using create-react-app" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="text/javascript">
//Don't run unless in an iframe
if (self !== top) {
var rootHeight;
setInterval(function () {
var rootElement = document.getElementById("root");
if (rootElement) {
var currentRootHeight = rootElement.offsetHeight;
//Only send values if height has changed since last time
if (rootHeight !== currentRootHeight) {
//postMessage to set iframe height
window.parent.postMessage({ "type": "frame-resized", "value": currentRootHeight }, '*');
rootHeight = currentRootHeight;
}
}
}
, 1000);
}
</script>
</body>
</html>
The code with setInterval can of course be modified but it works really well with dynamic content. setInterval only activates if the content is embedded in a iframe and postMessage only sends a message when height has changed.
You can read more about Window.postMessage() here but the description fits very good in what we want to achieve:
The window.postMessage() method safely enables cross-origin
communication between Window objects; e.g., between a page and a
pop-up that it spawned, or between a page and an iframe embedded
within it.
Normally, scripts on different pages are allowed to access each other
if and only if the pages they originate from share the same protocol,
port number, and host (also known as the "same-origin policy").
window.postMessage() provides a controlled mechanism to securely
circumvent this restriction (if used properly).
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

https://getbootstrap.com/docs/4.0/utilities/embed/
After a lot of research, it dawned on me, this is not a unique problem, I bet Bootstrap handles it. Lo and behold…

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>

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);
});

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.

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.

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

Related

How to dynamically resize an iFrame (crossbrowser solution)

I am trying to include an iframe on a Drupal page. So far I successfully included the frame but the height resize function that I wrote only works in Internet Explorer. My goal is to make resize work inFirefox and Chrome as well. I searched the Internet but I couldn't find what I was looking for. Long story short, I want my frame to automatically resize height itself.
This is what I did so far (this is the code of the HTML page included in Drupal):
<script type="text/javascript">
function resize() {
var iframe = document.all.icw;
document.getElementById("icw").style.height = iframe.document.body.scrollHeight + "px";
}
</script>
<iframe id="icw" src="XXXXX" width="100%" scrolling="no" onload="resize()">
I understand that if the iframe lives on another domain the solution may be more difficult due to permissions. Is that correct?
These are the errors I get in Chrome and Firefox:
Chrome: Cannot read property 'body' of undefined
Chrome: JavaScript attempt to access frame with URL XXX from frame with URL YYY. Domains, protocols and ports must match.
Firefox: document.all is undefined
EDIT: What if I want to change one of the iframe element? In my case I need to change a textbox value. I understand I must do that when the page is loaded but I can't find a solution. No matter where I put the code I always a get a
Cannot call method 'getElementById' of undefined
when I try to access the iFrame textbox.
EDIT2: Asked a new question since they weren't related.
This solution works even cross-domain as long as you have access to both. There are a bunch of edge cases, but at least you can start with this. You can also add timer event to make sure iFrame is always a correct size so you never have scrollbar. Keep in mind this only works with browser supporting HTML5 due to the use of postMessage.
Inside iFrame
function resize(){
var body = document.body,
html = document.documentElement,
height = body.offsetHeight;
if(height === 0){
height = html.offsetHeight;
}
parent.postMessage({'action':'RESIZE', 'height':height}, '*');
}
Parent
function handleMessage(e) {
if (e.data.action == 'RESIZE') {
var targetHeight = e.data.height;
$('#iFrame').height(targetHeight);
}
}
window.addEventListener("message", handleMessage, false);
window.addEventListener("message", handleMessage, false);
This code do not work for IE.
For IE use
window.attachEvent("onmessage", handleMessage, false);
Check out this little library that does this and manages all the other edge cases for you.
https://github.com/davidjbradshaw/iframe-resizer

Is there a way to determine if an iframe is visible/active

I have an iframe that runs on sites (with a different domain). Can I know within the iframe if it is viable to the user or if the page that this iframe "live in" is active or not.
Thanks.
This is actually not possible, because you are not able to access the main page that embeds the frame from the frame itself, if you are on a different domain / port / scheme.
If your iframe is on the same domain && port && scheme, you could do this:
<html>
<body>
<iframe src="frame.htm" id="myframe" style="display:block"></iframe>
</body>
</html>
And the frame.htm:
<script>
var is_hidden = parent.document.getElementById("myframe").style.display == "none";
alert(is_hidden ? "I am hidden" : "I am visible");
</script>
Update Overlooked the "from a different domain" - part of the question, updated the post accordingly.
You should check out the Intersection Observer API. I was able to get it to work for two different domains.
Something like this worked for me:
function handleIntersect(entries, observer) {
console.log('intersecting', entries[0].isIntersecting, 'visible', entries[0].isVisible);
}
let observer;
let options = {
root: null,
rootMargin: "0px",
threshold: [0.5]
};
observer = new IntersectionObserver(handleIntersect, options);
// The DOM element here should be a div that captures the whole content of the iframe.
observer.observe(DOM_ELEMENT);

Why isn't my iframe resizing to fit its content?

I'm attempting a cross-site iframe resize, following the instructions here, but the iframe remains unresized. I'm not sure what I'm doing wrong.
The iframe currently resides at http://ayeoui.com/testerpage.php. This makes a call to a blog post that in turn loads a "helper.html" page on the main ayeoui.com domain, which in theory should pass information back along to the first page and trigger a resize. The code on the main site says this:
<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('iframeid').height = parseInt(height)+60;
}
</script>
<iframe id='iframeid' src='http://rinich.com/blog/11/index.html'></iframe>
The linked page in turn has this code written on it:
<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.ayeoui.com/helper.html?height='+height+'&cacheb='+Math.random();
}
</script>
And finally, that leads us to the helper page:
<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>
Yet the tester page isn't changing a whit! Clearly I'm doing something wrong, but I'm not sure what. What's happening?
Uncaught SecurityError: Blocked a frame with origin "http://www.ayeoui.com" from accessing a frame with origin "http://ayeoui.com". Protocols, domains, and ports must match.
Your subdomains don't match. http://www.ayeoui.com is completely different from http://ayeoui.com.
Check your console for uncaught error type. You are redirecting from one page to another but the function resizeIframe is not working on the second page. You should make another .js file and call the function after linking that file. That should work.
I'm not sure, but this might be the reason: you're getting a same origin policy error.
The console in Chrome's dev tools logs this error: Uncaught SecurityError: Blocked a frame with origin "http://www.ayeoui.com" from accessing a frame with origin "http://ayeoui.com". Protocols, domains, and ports must match.
This is because www.ayeoui.com accesses rinich.com. This isn't allowed (why?).
You could try ensuring everything accesses from/to the same domain: ayeoui.com.
Also, on a separate note, you might want to consider adding the seamless attribute to your iframe, it just makes everything look so much neater (in my opinion):
<iframe id="iframeid" src="http://rinich.com/blog/11/index.html" seamless></iframe>

cross-domain iframe resizer?

I'm looking for a good cross-domain iframe resizing script that adjusts its height based on its content. I have access to the html/css for the source of the iframe as well. Is there any out there?
If your users are on modern browsers, you can solve this quite easily with postMessage in HTML5. Here's a quick solution which works well:
The iframe page:
<!DOCTYPE html>
<head>
</head>
<body onload="parent.postMessage(document.body.scrollHeight, 'http://target.domain.com');">
<h3>Got post?</h3>
<p>Lots of stuff here which will be inside the iframe.</p>
</body>
</html>
The parent page which contains the iframe (and would like to know its height):
<script type="text/javascript">
function resizeCrossDomainIframe(id, other_domain) {
var iframe = document.getElementById(id);
window.addEventListener('message', function(event) {
if (event.origin !== other_domain) return; // only accept messages from the specified domain
if (isNaN(event.data)) return; // only accept something which can be parsed as a number
var height = parseInt(event.data) + 32; // add some extra height to avoid scrollbar
iframe.height = height + "px";
}, false);
}
</script>
<iframe src='http://example.com/page_containing_iframe.html' id="my_iframe" onload="resizeCrossDomainIframe('my_iframe', 'http://example.com');">
</iframe>
Having failed to find a solution that dealt with all the different use-cases for this I ended up writing a simple js lib that supports both width and height, resizing content and multiple iframes on one page.
https://github.com/davidjbradshaw/iframe-resizer
EasyXDM can do just this :)
This blog post explains the gist of it
The first script on this page - the one using postMessage in HTML5 - also works for iframes on mobile - by resizing the iframe to the content - for example syndicating cross-domain - you can easily scroll in iphones or android, in a way that's not possible with iframes otherwise
After some research, I ended up using html5's message passing mechanism wrapped in a jQuery pluging that makes it compatible with older browsers using various methods (some of them described in this thread).
The end solution is very simple.
On the host (parent) page:
// executes when a message is received from the iframe, to adjust
// the iframe's height
$.receiveMessage(
function( event ){
$( 'my_iframe' ).css({
height: event.data
});
});
// Please note this function could also verify event.origin and other security-related checks.
On the iframe page:
$(function(){
// Sends a message to the parent window to tell it the height of the
// iframe's body
var target = parent.postMessage ? parent : (parent.document.postMessage ? parent.document : undefined);
$.postMessage(
$('body').outerHeight( true ) + 'px',
'*',
target
);
});
I've tested this on Chrome 13+, Firefox 3.6+, IE7, 8 and 9 on XP and W7, safari on OSX and W7. ;)
I have a whole different solution to cross domain iframe resizing. It involves procuring a copy of the target page that you will put in your iframe, writing it locally, then putting that copy into your iframe and resizing based on same domain access to the dom inside the frame.
an example follows:
<?php
if(isset($_GET['html'])) $blogpagehtml = file_get_contents(urldecode($_GET['html']));
else $blogpagehtml = file_get_contents('http://r****d.wordpress.com/');
$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML($blogpagehtml);
libxml_use_internal_errors(false);
$anchors = $doc->getElementsByTagName("a");
foreach($anchors as $anchor) {
$anchorlink=$anchor->getAttribute("href");
if(strpos($anchorlink,"http://r****d.wordpress")===false) $anchor->setAttribute("target","_top");
else $anchor->setAttribute("href","formatimportedblog.php?html=".urlencode($anchorlink));
}
$newblogpagehtml = $doc->saveHTML();
$token = rand(0,50);
file_put_contents('tempblog'.$token.'.html',$newblogpagehtml);
?>
<iframe id='iframe1' style='width:970px;margin:0 auto;' src='tempblog<?php echo $token; ?>.html' frameborder="0" scrolling="no" onLoad="autoResize('iframe1');" height="5600"></iframe>
Following code worked for me:
var iframe = document.getElementById(id);
iframe.height = iframe.contentDocument.body.scrollHeight;
Tested on Opera 11, IE 8 9, FF 8, Chrome 16

Resizing an iframe based on content

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.
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>
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>
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/
The simplest way using jQuery:
$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
$(this).css("height", $(this).contents().height() + "px");
});
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);
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.
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.
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
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.
If you have control over the iframe content , I strongly recommend using
ResizeObserver
Just insert the following at the end of srcdoc attribute of iframe ,
escape it if needed.
<script type="text/javascript">
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
// console.log(window.frameElement);
window.frameElement.style.height =cr.height +30+ "px";
}
});
ro.observe(document.body);
</script>
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!
<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>
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! :)
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.
When you want to zoom out a web page to fit it into the iframe size:
You should resize the iframe to fit it with the content
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>
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);
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);
}
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);
}
});
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.
I have been reading a lot of the answers here but nearly everyone gave some sort of cross-origin frame block.
Example error:
Uncaught DOMException: Blocked a frame with origin "null" from
accessing a cross-origin frame.
The same for the answers in a related thread:
Make iframe automatically adjust height according to the contents without using scrollbar?
I do not want to use a third party library like iFrame Resizer or similar library either.
The answer from #ChrisJacob is close but I'm missing a complete working example and not only links. #Selvamani and #latitov are good complements as well.
https://stackoverflow.com/a/3219970/3850405
I'm using width="100%" for the iframe but the code can be modified to work with width as well.
This is how I solved setting a custom height for the iframe:
Embedded iframe:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description"
content="Web site" />
<title>Test with embedded iframe</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<iframe id="ifrm" src="https://localhost:44335/package/details?key=123" width="100%"></iframe>
<script type="text/javascript">
window.addEventListener('message', receiveMessage, false);
function receiveMessage(evt) {
console.log("Got message: " + JSON.stringify(evt.data) + " from origin: " + evt.origin);
// Do we trust the sender of this message?
if (evt.origin !== "https://localhost:44335") {
return;
}
if (evt.data.type === "frame-resized") {
document.getElementById("ifrm").style.height = evt.data.value + "px";
}
}
</script>
</body>
</html>
iframe source, example from Create React App but only HTML and JS is used.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description"
content="Web site created using create-react-app" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="text/javascript">
//Don't run unless in an iframe
if (self !== top) {
var rootHeight;
setInterval(function () {
var rootElement = document.getElementById("root");
if (rootElement) {
var currentRootHeight = rootElement.offsetHeight;
//Only send values if height has changed since last time
if (rootHeight !== currentRootHeight) {
//postMessage to set iframe height
window.parent.postMessage({ "type": "frame-resized", "value": currentRootHeight }, '*');
rootHeight = currentRootHeight;
}
}
}
, 1000);
}
</script>
</body>
</html>
The code with setInterval can of course be modified but it works really well with dynamic content. setInterval only activates if the content is embedded in a iframe and postMessage only sends a message when height has changed.
You can read more about Window.postMessage() here but the description fits very good in what we want to achieve:
The window.postMessage() method safely enables cross-origin
communication between Window objects; e.g., between a page and a
pop-up that it spawned, or between a page and an iframe embedded
within it.
Normally, scripts on different pages are allowed to access each other
if and only if the pages they originate from share the same protocol,
port number, and host (also known as the "same-origin policy").
window.postMessage() provides a controlled mechanism to securely
circumvent this restriction (if used properly).
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
https://getbootstrap.com/docs/4.0/utilities/embed/
After a lot of research, it dawned on me, this is not a unique problem, I bet Bootstrap handles it. Lo and behold…
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>
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);
});
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.
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.
I have an easy solution and requires you to determine the width and height in the link, please try (It works with most browsers):
500x400

Categories