change iframe height according to content - javascript

iframe tag:
<iframe scrolling="no" style="height: 956px; width: 100%" frameborder="0" id="ampcontentiframe"></iframe>
i am trying to resize iframe height on content load.i tried other solutions from:
Link1
Link2
Link3
But none of them solved my problem.i tried calling resize function on window load as well as iframe load.but the height it sets every time is different(sometimes actual content height and sometimes original height of iframe).
Any help guys.....??
FYI:I also tried by removing scrolling attr from iframe.but it didn't work

This is one of those problems that really is harder than it should be. Here are the things you need to consider, in order to keep an iFrame sized correctly.
Working out an accurate height
Getting an accurate height for the iFrame is not as simple as it should be, as you have a choice of six different properties that you can check and none of them give a constantly right answer. The best solution I've come up with is this function that works so long as you don't use CSS to overflow the body tag.
function getIFrameHeight(){
function getComputedBodyStyle(prop) {
return parseInt(
document.defaultView.getComputedStyle(document.body, null),
10
);
}
return document.body.offsetHeight +
getComputedBodyStyle('marginTop') +
getComputedBodyStyle('marginBottom');
}
This is the IE9 version, for the much long IE8 version see this answer.
If you do overflow the body and you can't fix your code to stop this, then using either the offsetHeight or scrollHeight properties of document.documentElement are your best options. Both have pros and cons and it best just to test both and see which works for you.
PostMessage
The postMessage API provides a simple method for comunicating between an iFrame and it's parent.
To send a message to the parent page you call it as follows.
parent.postMessage('Hello parent','http://origin-domain.com');
In the other direction we can send the message to the iFrame with the following code.
var iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage('Hello my child', 'http://remote-domain.com:8080');
To recevie a message create an event listerner for the message event.
function receiveMessage(event)
{
if (event.origin !== "http://remote-domain.com:8080")
return;
console.log(event.data);
}
if ('addEventListener' in window){
window.addEventListener('message', receiveMessage, false);
} else if ('attachEvent' in window){ //IE
window.attachEvent('onmessage', receiveMessage);
These examples uses the origin property to limit where the message is sent to and to check where it came from. It is possible to specify * to allow sending to any domain and you may in some cases you may want to accept messages from any domain. However, if you do this you need to consider the security implications and implement your own checks on the incoming message to ensure it contains what your expecting. In this case the iframe can post it's height to '*', as we might have more than one parent domain. However, it's a good idea to check incoming messages are from the iFrame.
function isMessageFromIFrame(event,iframe){
var
origin = event.origin,
src = iframe.src;
if ((''+origin !== 'null') && (origin !== src.substr(0,origin.length))) {
throw new Error(
'Unexpect message received from: ' + origin +
' for ' + iframe.id + '. Message was: ' + event.data
);
}
return true;
}
MutationObserver
The other advance in more modern broswers is MutationObserver which allows you to watch for changes in the DOM; so it is now possible to detect changes that could effect the size of the iFrame without having to constantly poll with setInterval.
function createMutationObserver(){
var
target = document.querySelector('body'),
config = {
attributes : true,
attributeOldValue : false,
characterData : true,
characterDataOldValue : false,
childList : true,
subtree : true
},
observer = new MutationObserver(function(mutations) {
parent.postMessage('[iframeResize]'+document.body.offsetHeight,'*');
});
log('Setup MutationObserver');
observer.observe(target, config);
}
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
if (MutationObserver){
createMutationObserver();
}
Other issues
Other things to consider include, having more than one iFrame on the page, CSS :Checkbox and :Hover events causing page resize, avoiding the use of height auto in the iFrames' body and html tags and lastly the window being resized.
IFrame Resizer Library
I've wrapped all this up in a simple dependancy free library, that also provides some extra functions not discussed here.
https://github.com/davidjbradshaw/iframe-resizer
This works with IE8+.

You can try with the following script.
<script type="text/javascript">
function iframeLoaded() {
var iFrameID = document.getElementById('your_frame_id');
if(iFrameID) {
iFrameID.height = "";
iFrameID.height = iFrameID.contentWindow.document.body.scrollHeight + "px";
}
}
</script>
and add onload function to your iframe like
<iframe onload="iframeLoaded()">

Try with this if you want to scroll bar for iframe
<iframe style="height: 956px;overflow-y:scroll; width: 100%" frameborder="0" id="ampcontentiframe"></iframe>
just put "min-height" and add "overflow-y" prop as scroll it will work
or if you dont want to scroll then try like
<iframe scrolling="no" style="min-height: 956px; width: 100%" frameborder="0" id="ampcontentiframe"></iframe>

Related

How can I resize the iframe without page refresh?

I have some swf embedded in iframe but only if the page is refreshed the iframe is resized, then if I select other one then will show as all swf not only the animation the background as well. This is what I am using
if ( 'resizeIframe' === $('#onPlayAction').val() ) {
var ifrEl = $('div.player-container iframe.page-iframe')[0];
$(ifrEl).show();
ifrEl.src = htmlPageBrowserUri;
ifrEl.onload = function() {
ifrEl.width = ifrEl.contentWindow.document.body.scrollWidth;
ifrEl.height = ifrEl.contentWindow.document.body.scrollHeight;
}
}
There are three ways to do this.
You can change the size on every window resize
$(window).on('resize', function (){
ifrEl.width = ... ;
ifrEl.height = ... ;
})
You can use some jQuery plugins like iFrame Resizer
You can use some nifty css tricks. Go search for responsive iframes using css and you will find a ton of good answers.
I hope this all helps you.
I suspect the issue with your code might be thses two lines :
ifrEl.src = htmlPageBrowserUri;
ifrEl.onload = function() {
The problem being that the first line set s the frame address, but second line sets the onload event immediately, probably before the page has loaded ? So when the page does load, the line setting onload event has already run & so doens't get set.
I don't quite understand the text in your question (sorry!) but the code below successfully resizes an iframe - it's run 'onload' in the frame's page:
<body onload="setParent()">
In case it's relevant, the iframe itself has attributes:
<iframe id="neckfinishframe" style="width:100%;overflow-x:hidden" src=".. etc">
In my case I'm only concerned about height. Width is 100%.
In the iFrame page, this code runs from the onload event to amend the iframe height to be whatever the height of the page is, plus a bit. This is intended to avoid showing a set of scroll bars within the iframe.
function setParent() {
// runs onload in iframe page
// in my case I have to run it from the frame page because I need to know the page rendered height in order to set the iframe height
var f;
try {f = parent.getElementById("neckfinishframe")} catch (e) {};
if (f != null) f.style.height=(this.document.body.scrollHeight+30)+"px";
}
Note - I haven't tried this cross- browser but I know it works in IE.

cross domain iframe resize for firefox 3.6.10

Is there any way to resize a cross domain iframe according to its content, that would work in Firefox 3.6.10?
I thought that the postMessage command works, but a solution I found works in Firefox 12 but not in Firefox 3.6.10. Or maybe that's not the problem.
As I wrote in another question, youtube seems to be just embedding the content of the iframe in the page for the comment section, that's how it gets resized dynamically. And this would basically solve every iframe issue too, since the HTML would be embedded in the website's local HTML, and no same origin policy would block anything. When I asked about this, I didn't get answers though.
So thank you in advance, or if you don't like when people say that, I would be grateful if someone would helped.
And I CAN control the content inside the frame too!
(I need this for a script that makes youtube look like around 2012, so more people could be grateful too.)
You might try looking at this on GitHub.
https://github.com/davidjbradshaw/iframe-resizer
solution i found/modified:
in the page which contains the iframe:
<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;
if (isNaN(event.data)) return;
var height = parseInt(event.data) + 32;
iframe.height = height + "px";
}, false);
}
in the iframe code:
<iframe id="my_iframe" onload="resizeCrossDomainIframe('my_iframe', '**whatever domain your iframe content is on**');"></iframe>
for continuous resizing, i put this in the page containing the iframe:
<script>
var interval = setInterval(function(){
resizeCrossDomainIframe('my_iframe', '**whatever domain your iframe content is on**');
}, 100);
</script>
on the iframe content page:
<script>
window.onload = function() {
window.parent.postMessage(document.body.scrollHeight, '**whatever domain your iframe containing page is on**');
}
</script>
and this for continuous resizing:
<script>
var interval2 = setInterval(function(){
window.parent.postMessage(document.body.scrollHeight, '**whatever domain your iframe containing page is on**');
}, 100);
</script>
CORRECTION: the first interval script isnt needed, since the eventlistener executes the function every time a message is sent to it. it would just cause slowness

Resize Parent Frame to fit iFrame Height [duplicate]

I tried a few solutions but wasn't successful. I'm wondering if there is a solution out there preferably with an easy-to-follow tutorial.
You have three alternatives:
1. Use iFrame-resizer
This is a simple library for keeping iFrames sized to their content. It uses the PostMessage and MutationObserver APIs, with fall backs for IE8-10. It also has options for the content page to request the containing iFrame is a certain size and can also close the iFrame when your done with it.
https://github.com/davidjbradshaw/iframe-resizer
2. Use Easy XDM (PostMessage + Flash combo)
Easy XDM uses a collection of tricks for enabling cross-domain communication between different windows in a number of browsers, and there are examples for using it for iframe resizing:
http://easyxdm.net/wp/2010/03/17/resize-iframe-based-on-content/
http://kinsey.no/blog/index.php/2010/02/19/resizing-iframes-using-easyxdm/
Easy XDM works by using PostMessage on modern browsers and a Flash based solution as fallback for older browsers.
See also this thread on Stackoverflow (there are also others, this is a commonly asked question). Also, Facebook would seem to use a similar approach.
3. Communicate via a server
Another option would be to send the iframe height to your server and then poll from that server from the parent web page with JSONP (or use a long poll if possible).
I got the solution for setting the height of the iframe dynamically based on it's content. This works for the cross domain content.
There are some steps to follow to achieve this.
Suppose you have added iframe in "abc.com/page" web page
<div>
<iframe id="IframeId" src="http://xyz.pqr/contactpage" style="width:100%;" onload="setIframeHeight(this)"></iframe>
</div>
Next you have to bind windows "message" event under web page "abc.com/page"
window.addEventListener('message', function (event) {
//Here We have to check content of the message event for safety purpose
//event data contains message sent from page added in iframe as shown in step 3
if (event.data.hasOwnProperty("FrameHeight")) {
//Set height of the Iframe
$("#IframeId").css("height", event.data.FrameHeight);
}
});
On iframe load you have to send message to iframe window content with "FrameHeight" message:
function setIframeHeight(ifrm) {
var height = ifrm.contentWindow.postMessage("FrameHeight", "*");
}
On main page that added under iframe here "xyz.pqr/contactpage" you have to bind windows "message" event where all messages are going to receive from parent window of "abc.com/page"
window.addEventListener('message', function (event) {
// Need to check for safety as we are going to process only our messages
// So Check whether event with data(which contains any object) contains our message here its "FrameHeight"
if (event.data == "FrameHeight") {
//event.source contains parent page window object
//which we are going to use to send message back to main page here "abc.com/page"
//parentSourceWindow = event.source;
//Calculate the maximum height of the page
var body = document.body, html = document.documentElement;
var height = Math.max(body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight);
// Send height back to parent page "abc.com/page"
event.source.postMessage({ "FrameHeight": height }, "*");
}
});
What I did was compare the iframe scrollWidth until it changed size while i incrementally set the IFrame Height. And it worked fine for me. You can adjust the increment to whatever is desired.
<script type="text/javascript">
function AdjustIFrame(id) {
var frame = document.getElementById(id);
var maxW = frame.scrollWidth;
var minW = maxW;
var FrameH = 100; //IFrame starting height
frame.style.height = FrameH + "px"
while (minW == maxW) {
FrameH = FrameH + 100; //Increment
frame.style.height = FrameH + "px";
minW = frame.scrollWidth;
}
}
</script>
<iframe id="RefFrame" onload="AdjustIFrame('RefFrame');" class="RefFrame"
src="http://www.YourUrl.com"></iframe>
I have a script that drops in the iframe with it's content. It also makes sure that iFrameResizer exists (it injects it as a script) and then does the resizing.
I'll drop in a simplified example below.
// /js/embed-iframe-content.js
(function(){
// Note the id, we need to set this correctly on the script tag responsible for
// requesting this file.
var me = document.getElementById('my-iframe-content-loader-script-tag');
function loadIFrame() {
var ifrm = document.createElement('iframe');
ifrm.id = 'my-iframe-identifier';
ifrm.setAttribute('src', 'http://www.google.com');
ifrm.style.width = '100%';
ifrm.style.border = 0;
// we initially hide the iframe to avoid seeing the iframe resizing
ifrm.style.opacity = 0;
ifrm.onload = function () {
// this will resize our iframe
iFrameResize({ log: true }, '#my-iframe-identifier');
// make our iframe visible
ifrm.style.opacity = 1;
};
me.insertAdjacentElement('afterend', ifrm);
}
if (!window.iFrameResize) {
// We first need to ensure we inject the js required to resize our iframe.
var resizerScriptTag = document.createElement('script');
resizerScriptTag.type = 'text/javascript';
// IMPORTANT: insert the script tag before attaching the onload and setting the src.
me.insertAdjacentElement('afterend', ifrm);
// IMPORTANT: attach the onload before setting the src.
resizerScriptTag.onload = loadIFrame;
// This a CDN resource to get the iFrameResizer code.
// NOTE: You must have the below "coupled" script hosted by the content that
// is loaded within the iframe:
// https://unpkg.com/iframe-resizer#3.5.14/js/iframeResizer.contentWindow.min.js
resizerScriptTag.src = 'https://unpkg.com/iframe-resizer#3.5.14/js/iframeResizer.min.js';
} else {
// Cool, the iFrameResizer exists so we can just load our iframe.
loadIFrame();
}
}())
Then the iframe content can be injected anywhere within another page/site by using the script like so:
<script
id="my-iframe-content-loader-script-tag"
type="text/javascript"
src="/js/embed-iframe-content.js"
></script>
The iframe content will be injected below wherever you place the script tag.
Hope this is helpful to someone. 👍
I ran into this issue while working on something at work (using React). Basically, we have some external html content that we save into our document table in the database and then insert onto the page under certain circumstances when you're in the Documents dataset.
So, given n inlines, of which up to n could contain external html, we needed to devise a system to automatically resize the iframe of each inline once the content fully loaded in each. After spinning my wheels for a bit, this is how I ended up doing it:
Set a message event listener in the index of our React app which checks for a a specific key that we will set from the sender iframe.
In the component that actually renders the iframes, after inserting the external html into it, I append a <script> tag that will wait for the iframe's window.onload to fire. Once that fires, we use postMessage to send a message to the parent window with information about the iframe id, computed height, etc.
If the origin matches and the key is satisfied in the index listener, grab the DOM id of the iframe that we pass in the MessageEvent object
Once we have the iframe, just set the height from the value that is passed from the iframe postMessage.
// index
if (window.postMessage) {
window.addEventListener("message", (messageEvent) => {
if (
messageEvent.data.origin &&
messageEvent.data.origin === "company-name-iframe"
) {
const iframe = document.getElementById(messageEvent.data.id)
// this is the only way to ensure that the height of the iframe container matches its body height
iframe.style.height = `${messageEvent.data.height}px`
// by default, the iframe will not expand to fill the width of its parent
iframe.style.width = "100%"
// the iframe should take precedence over all pointer events of its immediate parent
// (you can still click around the iframe to segue, for example, but all content of the iframe
// will act like it has been directly inserted into the DOM)
iframe.style.pointerEvents = "all"
// by default, iframes have an ugly web-1.0 border
iframe.style.border = "none"
}
})
}
// in component that renders n iframes
<iframe
id={`${props.id}-iframe`}
src={(() => {
const html = [`data:text/html,${encodeURIComponent(props.thirdLineData)}`]
if (window.parent.postMessage) {
html.push(
`
<script>
window.onload = function(event) {
window.parent.postMessage(
{
height: document.body.scrollHeight,
id: "${props.id}-iframe",
origin: "company-name-iframe",
},
"${window.location.origin}"
);
};
</script>
`
)
}
return html.join("\n")
})()}
onLoad={(event) => {
// if the browser does not enforce a cross-origin policy,
// then just access the height directly instead
try {
const { target } = event
const contentDocument = (
target.contentDocument ||
// Earlier versions of IE or IE8+ where !DOCTYPE is not specified
target.contentWindow.document
)
if (contentDocument) {
target.style.height = `${contentDocument.body.scrollHeight}px`
}
} catch (error) {
const expectedError = (
`Blocked a frame with origin "${window.location.origin}" ` +
`from accessing a cross-origin frame.`
)
if (error.message !== expectedError) {
/* eslint-disable no-console */
console.err(
`An error (${error.message}) ocurred while trying to check to see ` +
"if the inner iframe is accessible or not depending " +
"on the browser cross-origin policy"
)
}
}
}}
/>
Here is an alternative implementation.
Basically if you able to edit page at other domain you can place another iframe page that belongs to your server which saving height to cookies.
With an interval read cookies when it is updated, update the height of the iframe. That is all.
Edit: 2019 December
The solution above basically uses another iframe inside of an iframe 3rd iframe is belongs to the top page domain, which you call this page with a query string that saves size value to a cookie, outer page checks this query with some interval. But it is not a good solution so you should follow this one:
In Top page :
window.addEventListener("message", (m)=>{iframeResizingFunction(m)});
Here you can check m.origin to see where is it comes from.
In frame page:
window.parent.postMessage({ width: 640, height:480 }, "*")
Although, please don't forget this is not so secure way. To make it secure update * value (targetOrigin) with your desired value.
Please follow documentation: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
I found another server side solution for web dev using PHP to get the size of an iframe.
First is using server script PHP to an external call via internal function: (like a file_get_contents with but curl and dom).
function curl_get_file_contents($url,$proxyActivation=false) {
global $proxy;
$c = curl_init();
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.1.7) Gecko/20070914 Firefox/2.0.0.7");
curl_setopt($c, CURLOPT_REFERER, $url);
curl_setopt($c, CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
if($proxyActivation) {
curl_setopt($c, CURLOPT_PROXY, $proxy);
}
$contents = curl_exec($c);
curl_close($c);
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
#$dom->loadHTML($contents);
$form = $dom->getElementsByTagName("body")->item(0);
if ($contents) //si on a du contenu
return $dom->saveHTML();
else
return FALSE;
}
$url = "http://www.google.com"; //Exernal url test to iframe
<html>
<head>
<script type="text/javascript">
</script>
<style type="text/css">
#iframe_reserve {
width: 560px;
height: 228px
}
</style>
</head>
<body>
<div id="iframe_reserve"><?php echo curl_get_file_contents($url); ?></div>
<iframe id="myiframe" src="http://www.google.com" scrolling="no" marginwidth="0" marginheight="0" frameborder="0" style="overflow:none; width:100%; display:none"></iframe>
<script type="text/javascript">
window.onload = function(){
document.getElementById("iframe_reserve").style.display = "block";
var divHeight = document.getElementById("iframe_reserve").clientHeight;
document.getElementById("iframe_reserve").style.display = "none";
document.getElementById("myiframe").style.display = "block";
document.getElementById("myiframe").style.height = divHeight;
alert(divHeight);
};
</script>
</body>
</html>
You need to display under the div (iframe_reserve) the html generated by the function call by using a simple echo curl_get_file_contents("location url iframe","activation proxy")
After doing this a body event function onload with javascript take height of the page iframe just with a simple control of the content div (iframe_reserve)
So I used divHeight = document.getElementById("iframe_reserve").clientHeight; to get height of the page external we are going to call after masked the div container (iframe_reserve). After this we load the iframe with its good height that's all.

iframe Auto Adjust Height as content changes

I have an iframe as you can see on the following link;-
http://one2onecars.com
The iframe is the online booking in the centre of the screen. The problem I have is that although the height of the iframe is okay as the page loads, I need it to somehow auto adjust the height as the page content adjusts. For example, if I do a postcode search in the online booking it creates a dropdown menu and then makes the 'Next Step' button not viewable.
What I need to happen is that when the content of the online booking changes, the iframe auto adjusts to the new height of the iframe (dynamically) as it is not loading any other pages.
I have tried several different scripts using jquery to try resolving this issue, but they all only seem to auto adjust the height of the iframe when the page first loads and not as the contents of the iframe changes.
Is this even possible to do?
The code I have at the moment is with a set height at the moment:-
<div id="main-online-booking">
<iframe id="main-online-frame" class="booking-dimensions" src="http://www.marandy.com/one2oneob/login-guest.php" scrolling="no" frameborder="0"></iframe>
</div>
#main-online-booking {
height: 488px;
border-bottom: 6px #939393 solid;
border-left: 6px #939393 solid;
border-right: 6px #939393 solid;
z-index: 4;
background-color: #fff;
}
.booking-dimensions {
width: 620px;
height: 488px;
}
If anybody can help me with this I would be much appreciated!
setInterval
The only (corrected due to advances in browser tech, see David Bradshaw's answer) backwards compatible way to achieve this with an iframe is to use setInterval and keep an eye on the iframe's content yourself. When it changes its height, you update the size of the iframe. There is no such event you can listen out for that will make it easy unfortunately.
A basic example, this will only work if the iframe content that has changed in size is part of the main page flow. If the elements are floated or positioned then you will have to target them specifically to look for height changes.
jQuery(function($){
var lastHeight = 0, curHeight = 0, $frame = $('iframe:eq(0)');
setInterval(function(){
curHeight = $frame.contents().find('body').height();
if ( curHeight != lastHeight ) {
$frame.css('height', (lastHeight = curHeight) + 'px' );
}
},500);
});
Obviously depending on what you want you can modify the perspective of this code so that it works from the iframe, on itself, rather than expecting to be part of the main page.
cross-domain issue
The problem you will find is that due to browser security it wont let you access the content of the iframe if it is on a different host to the main page, so there isn't actually anything you can do unless you have a way of adding any script to the html that appears in the iframe.
ajax
Some others are suggesting trying to use the third-party service via AJAX, unless the service supports this method it will be very unlikely you'll be able to get it to work -- especially if it is a booking service that will most likely need to operate over https/ssl.
As it appears you have full control over the iframe content, you have full options open to you, AJAX with JSONP would be an option. However, one word of warning. If your booking system is multistepped you need to make sure you have a well designed UI -- and possibly some history/fragment management code -- if you are to go down the AJAX route. All because you can never tell when a user will decide to navigate forward or back in their browser (which an iframe would automatically handle, within reason). A well designed UI can detract users from doing this.
cross-domain communication
If you have control of both sides (which it sounds like you do) you also have the cross domain communication option using window.postMessage - see here for more information https://developer.mozilla.org/en-US/docs/DOM/window.postMessage
Modern browser and in part IE8 have some new features that make this task easier than it use to be.
PostMessage
The postMessage API provides a simple method for comunicating between an iFrame and it's parent.
To send a message to the parent page you call it as follows.
parent.postMessage('Hello parent','http://origin-domain.com');
In the other direction we can send the message to the iFrame with the following code.
var iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage('Hello my child', 'http://remote-domain.com:8080');
To recevie a message create an event listerner for the message event.
function receiveMessage(event)
{
if (event.origin !== "http://remote-domain.com:8080")
return;
console.log(event.data);
}
if ('addEventListener' in window){
window.addEventListener('message', receiveMessage, false);
} else if ('attachEvent' in window){ //IE
window.attachEvent('onmessage', receiveMessage);
These examples uses the origin property to limit where the message is sent to and to check where it came from. It is possible to specify * to allow sending to any domain and you may in some cases you may want to accept messages from any domain. However, if you do this you need to consider the security implications and implement your own checks on the incoming message to ensure it contains what your expecting. In this case the iframe can post it's height to '*', as we might have more than one parent domain. However, it's a good idea to check incoming messages are from the iFrame.
function isMessageFromIFrame(event,iframe){
var
origin = event.origin,
src = iframe.src;
if ((''+origin !== 'null') && (origin !== src.substr(0,origin.length))) {
throw new Error(
'Unexpect message received from: ' + origin +
' for ' + iframe.id + '. Message was: ' + event.data
);
}
return true;
}
MutationObserver
The other advance in more modern broswers is MutationObserver which allows you to watch for changes in the DOM; so it is now possible to detect changes that could effect the size of the iFrame without having to constantly poll with setInterval.
function createMutationObserver(){
var
target = document.querySelector('body'),
config = {
attributes : true,
attributeOldValue : false,
characterData : true,
characterDataOldValue : false,
childList : true,
subtree : true
},
observer = new MutationObserver(function(mutations) {
parent.postMessage('[iframeResize]'+document.body.offsetHeight,'*');
});
log('Setup MutationObserver');
observer.observe(target, config);
}
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
if (MutationObserver){
createMutationObserver();
}
Working out an accurate height
Getting an accurate height for the iFrame is not as simple as it should be, as you have a choice of six different properties that you can check and none of them give a constantly right answer. The best solution I've come up with is this function that works so long as you don't use CSS to overflow the body tag.
function getIFrameHeight(){
function getComputedBodyStyle(prop) {
return parseInt(
document.defaultView.getComputedStyle(document.body, null),
10
);
}
return document.body.offsetHeight +
getComputedBodyStyle('marginTop') +
getComputedBodyStyle('marginBottom');
}
This is the IE9 version, for the much long IE8 version see this answer.
If you do overflow the body and you can't fix your code to stop this, then using either the offsetHeight or scrollHeight properties of document.documentElement are your best options. Both have pros and cons and it best just to test both and see which works for you.
Other issues
Other things to consider include, having more than one iFrame on the page, CSS :Checkbox and :Hover events causing page resize, avoiding the use of height auto in the iFrames' body and html tags and lastly the window being resized.
IFrame Resizer Library
I've wrapped all this up in a simple dependancy free library, that also provides some extra functions not discussed here.
https://github.com/davidjbradshaw/iframe-resizer
This works with IE8+.
I wrote this script and it's working perfectly for me. Feel free to use it!
function ResizeIframeFromParent(id) {
if (jQuery('#'+id).length > 0) {
var window = document.getElementById(id).contentWindow;
var prevheight = jQuery('#'+id).attr('height');
var newheight = Math.max( window.document.body.scrollHeight, window.document.body.offsetHeight, window.document.documentElement.clientHeight, window.document.documentElement.scrollHeight, window.document.documentElement.offsetHeight );
if (newheight != prevheight && newheight > 0) {
jQuery('#'+id).attr('height', newheight);
console.log("Adjusting iframe height for "+id+": " +prevheight+"px => "+newheight+"px");
}
}
}
You can call the function inside a loop:
<script>
jQuery(document).ready(function() {
// Try to change the iframe size every 2 seconds
setInterval(function() {
ResizeIframeFromParent('iframeid');
}, 2000);
});
</script>
ResizeObserver allows your code to remain encapsulated inside the iframe and decoupled from the outer scope (i.e., versus postMessage), and it is ligher weight than the general-purpose MutationObserver.
Simple example below (or good example at Mozilla):
const myElement = document.getElementById('my-element');
const resizeObserver = new ResizeObserver((entries) => {
const dims = myElement.getBoundingClientRect(); // or see Mozilla for `entries` example
console.log(`new height (${dims.height}) and width (${dims.width})`);
});
resizeObserver.observe(myElement);
use this script:
$(document).ready(function () {
// Set specific variable to represent all iframe tags.
var iFrames = document.getElementsByTagName('iframe');
// Resize heights.
function iResize() {
// Iterate through all iframes in the page.
for (var i = 0, j = iFrames.length; i < j; i++) {
// Set inline style to equal the body height of the iframed content.
iFrames[i].style.height = iFrames[i].contentWindow.document.body.offsetHeight + 'px';
}
}
// Check if browser is Safari or Opera.
if ($.browser.safari || $.browser.opera) {
// Start timer when loaded.
$('iframe').load(function () {
setTimeout(iResize, 0);
});
// Safari and Opera need a kick-start.
for (var i = 0, j = iFrames.length; i < j; i++) {
var iSource = iFrames[i].src;
iFrames[i].src = '';
iFrames[i].src = iSource;
}
} else {
// For other good browsers.
$('iframe').load(function () {
// Set inline style to equal the body height of the iframed content.
this.style.height = this.contentWindow.document.body.offsetHeight + 'px';
});
}
});
Note : use it on webserver.

Computing a cross-browser iframe height

One of the most difficult problems in my javascript experience has been the correct (that is "cross-browser") computing of a iframe height.
In my applications I have a lot of dynamically generated iframe and I want them all do a sort of autoresize at the end of the load event to adjust their height and width.
In the case of height computing my best solution is the following (with the help of jQuery):
function getDocumentHeight(doc) {
var mdoc = doc || document;
if (mdoc.compatMode=='CSS1Compat') {
return mdoc.body.offsetHeight;
}
else {
if ($.browser.msie)
return mdoc.body.scrollHeight;
else
return Math.max($(mdoc).height(), $(mdoc.body).height());
}
}
I searched the internet without success. I also tested Yahoo library that has some methods for document and viewport dimensions, but it's not satisfactory.
My solution works decently, but sometimes it calculates a taller height.
I've studied and tested tons of properties regarding document height in Firefox/IE/Safari: documentElement.clientHeight, documentElement.offsetHeight, documentElement.scrollHeight, body.offsetHeight, body.scrollHeight, ...
Also jQuery doesn't have a coherent behavior in various browser with the calls $(document.body).height(), $('html', doc).height(), $(window).height()
I call the above function not only at the end of load event, but also in the case of dynamically inserted DOM elements or elements hidden or shown. This is a case that sometimes breaks the code that works only in the load event.
Does someone have a real cross-browser (at least Firefox/IE/Safari) solution? Some tips or hints?
Although I like your solution, I've always found IFRAMEs to be more trouble than they're worth.
Why ? 1. The sizing issue. 2. the iframe has that src attribute to worry about. i.e. absolute path. 3. the extra complexity with the pages.
My solution - DIVs which are dynamically loaded through AJAX calls. DIVs will auto size. Although the AJAX code requires javascript work (which can be done through frameworks) they are relative to where the site is. 3 is a wash, you're trading complexity in pages up to javascript.
Instead of <IFRAME ...> use <DIV id="mystuff" />
Do the ajax call to fill the mystuff div with data and let the browser worry about it.
This has been without an accepted answer for awhile, so I wanted to contribute the solution I ended up going with after some research. This is cross-browser and cross-domain (e.g. when the iframe points to content from a different domain)
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. ;)
Since in your example you're accessing the document inside the IFRAME I guess you're talking about knowing the height of the document and not of the frame itself. Also, that means that the content comes from your own website and you have control over it's contents.
So, why don't you simply place a DIV around your content and then use the clientHeight of that DIV?
Code you load in your IFRAME:
...
<body>
<div id="content">
...
</div>
</body>
The parent document:
function getDocumentHeight(mdoc) {
return mdoc.getElementById("content").clientHeight;
}
BTW, this part of your example function does not make sense as "document" does not refer to the IFRAME:
var mdoc = doc || document;
Here is a solution that seems to work. Basically, the scrollHeight is the correct value in most cases. However, in IE (specifically 6 and 7), if the content is simply contained in text nodes, the height is not calculated and just defaults to the height set in CSS or on the "height" attribute on the iframe. This was found through trial and error, so take it for what it is worth.
function getDocumentHeight(doc) {
var mdoc = doc || document;
var docHeight = mdoc.body.scrollHeight;
if ($.browser.msie) {
// IE 6/7 don't report body height correctly.
// Instead, insert a temporary div containing the contents.
var child = $("<div>" + mdoc.body.innerHTML + "</div>", mdoc);
$("body", mdoc).prepend(child);
docHeight = child.height();
child.remove();
}
return docHeight;
}
I have found this solution to work in ie 6+, ff 3.5+, safari 4+. I am creating and appending an iframe to the document. The following code is executed at the end of jQuery's load event after some other dom manipulation. The timeout is needed for me because of the additional dom manipulation taking place in the load event.
// sizing - slight delay for good scrollheight
setTimeout(function() {
var intContentHeight = objContentDoc.body.scrollHeight;
var $wrap = $("#divContentWrapper", objContentFrameDoc);
var intMaxHeight = getMaxLayeredContentHeight($wrap);
$this.height(intContentHeight > intMaxHeight ? intMaxHeight : intContentHeight);
// animate
fireLayeredContentAnimation($wrap);
}, 100);
I have some sizing constraints to consider, which is what the getMaxLayeredContentHeight call is checking for me. Hope that helps.
Here's a script that resizes the iFrame depending on the body inside it's height.

Categories

Resources