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.
Related
For say i have a Site called example.com on which iframe is embedded of domain iframe.net, now i want to read the content of iframe and pass some parameter to display a textual message. Like Hi with username.
Now the problem is this able not able to make connection between the two , even am not able to get the innerHTML of iframe i used following approach
document.getElementById('myframe').contentWindow.document.body.innerHTML;
It throws error "Permission denied to access property"
Do anyone know how to read and write in cross domain platform
If you don't have control over the framed site, you cannot circumvent the cross-domain policy.
If you have control over both sites, you can use the postMessage method to transfer data across different domains. A very basic example:
// framed.htm:
window.onmessage = function(event) {
event.source.postMessage(document.body.innerHTML, event.origin);
};
// Main page:
window.onmessage = function(event) {
alert(event.data);
};
// Trigger:
// <iframe id="myframe" src="framed.htm"></iframe>
document.getElementById('myframe').contentWindow.postMessage('','*');
In Internet Explorer 8, events passed as a parameter may be null, that is why you need to access the event in a different manner:
In frame.html:
window.onmessage = function(event) {
var evt = event || window.event;
evt.source.postMessage('Message from iFrame', evt.origin);
};
On main.html:
window.onmessage = function(event) {
var evt = event || window.event;
alert(evt.data);
};
The event is triggered the same way as Rob W has presented:
document.getElementById('frameId').contentWindow.postMessage('message','*');
It can happen only if you have control for both sites
you can use postMessage
here's how you can apply it
first, you can add the below code to the site you want to retrieve data from
<script>
function test() {
document.getElementById('idMyIframe').contentWindow.postMessage("your message here", "*");
}
</script>
<iframe id="idMyIframe" onload="test()" src="http://example.otherdomaintosenddatato.com"></iframe>
the second step you can add this code below to the other domain you want to send a message or the data to it
window.addEventListener("message", function (message) {
if (message.origin == "http://firstdomain.com") {
console.log(message)
}
});
and please note you must add the test() function on the onload attribute of the iframe cause if the function runs before the iframe was loaded it will become useless and it won't send data
Cross Domain iframe elements access in React with (srcDoc):-
<iframe srcDoc={this.state.HTML} width='100%' id='iframetnd' onLoad={this.onclickinsideiframe}/>
onclickinsideiframe=()=>{
if(document.getElementById('iframetnd')!==null){
let iframe = document.getElementById('iframetnd');
let innerDocument = (iframe.contentDocument)
? iframe.contentDocument
: iframe.contentWindow.document;
let Obj = innerDocument.getElementsByClassName("navButtons")[0];
if(Obj !== undefined){
Obj.onclick = ()=>this.func();
}
}
}
func=()=>{
this.setState({page:2})
this.arrangeTest();
}
iFrame does not allow to access contents from Cross Domain platform. You can only access if your iFrame is using the same domain.
This solution works same as iFrame. I have created a PHP script that can get all the contents from the other website, and most important part is you can easily apply your custom jQuery to that external content. Please refer to the following script that can get all the contents from the other website and then you can apply your cusom jQuery/JS as well. This content can be used anywhere, inside any element or any page.
<div id='myframe'>
<?php
/*
Use below function to display final HTML inside this div
*/
//Display Frame
echo displayFrame();
?>
</div>
<?php
/*
Function to display frame from another domain
*/
function displayFrame()
{
$webUrl = 'http://[external-web-domain.com]/';
//Get HTML from the URL
$content = file_get_contents($webUrl);
//Add custom JS to returned HTML content
$customJS = "
<script>
/* Here I am writing a sample jQuery to hide the navigation menu
You can write your own jQuery for this content
*/
//Hide Navigation bar
jQuery(\".navbar.navbar-default\").hide();
</script>";
//Append Custom JS with HTML
$html = $content . $customJS;
//Return customized HTML
return $html;
}
For say i have a Site called example.com on which iframe is embedded of domain iframe.net, now i want to read the content of iframe and pass some parameter to display a textual message. Like Hi with username.
Now the problem is this able not able to make connection between the two , even am not able to get the innerHTML of iframe i used following approach
document.getElementById('myframe').contentWindow.document.body.innerHTML;
It throws error "Permission denied to access property"
Do anyone know how to read and write in cross domain platform
If you don't have control over the framed site, you cannot circumvent the cross-domain policy.
If you have control over both sites, you can use the postMessage method to transfer data across different domains. A very basic example:
// framed.htm:
window.onmessage = function(event) {
event.source.postMessage(document.body.innerHTML, event.origin);
};
// Main page:
window.onmessage = function(event) {
alert(event.data);
};
// Trigger:
// <iframe id="myframe" src="framed.htm"></iframe>
document.getElementById('myframe').contentWindow.postMessage('','*');
In Internet Explorer 8, events passed as a parameter may be null, that is why you need to access the event in a different manner:
In frame.html:
window.onmessage = function(event) {
var evt = event || window.event;
evt.source.postMessage('Message from iFrame', evt.origin);
};
On main.html:
window.onmessage = function(event) {
var evt = event || window.event;
alert(evt.data);
};
The event is triggered the same way as Rob W has presented:
document.getElementById('frameId').contentWindow.postMessage('message','*');
It can happen only if you have control for both sites
you can use postMessage
here's how you can apply it
first, you can add the below code to the site you want to retrieve data from
<script>
function test() {
document.getElementById('idMyIframe').contentWindow.postMessage("your message here", "*");
}
</script>
<iframe id="idMyIframe" onload="test()" src="http://example.otherdomaintosenddatato.com"></iframe>
the second step you can add this code below to the other domain you want to send a message or the data to it
window.addEventListener("message", function (message) {
if (message.origin == "http://firstdomain.com") {
console.log(message)
}
});
and please note you must add the test() function on the onload attribute of the iframe cause if the function runs before the iframe was loaded it will become useless and it won't send data
Cross Domain iframe elements access in React with (srcDoc):-
<iframe srcDoc={this.state.HTML} width='100%' id='iframetnd' onLoad={this.onclickinsideiframe}/>
onclickinsideiframe=()=>{
if(document.getElementById('iframetnd')!==null){
let iframe = document.getElementById('iframetnd');
let innerDocument = (iframe.contentDocument)
? iframe.contentDocument
: iframe.contentWindow.document;
let Obj = innerDocument.getElementsByClassName("navButtons")[0];
if(Obj !== undefined){
Obj.onclick = ()=>this.func();
}
}
}
func=()=>{
this.setState({page:2})
this.arrangeTest();
}
iFrame does not allow to access contents from Cross Domain platform. You can only access if your iFrame is using the same domain.
This solution works same as iFrame. I have created a PHP script that can get all the contents from the other website, and most important part is you can easily apply your custom jQuery to that external content. Please refer to the following script that can get all the contents from the other website and then you can apply your cusom jQuery/JS as well. This content can be used anywhere, inside any element or any page.
<div id='myframe'>
<?php
/*
Use below function to display final HTML inside this div
*/
//Display Frame
echo displayFrame();
?>
</div>
<?php
/*
Function to display frame from another domain
*/
function displayFrame()
{
$webUrl = 'http://[external-web-domain.com]/';
//Get HTML from the URL
$content = file_get_contents($webUrl);
//Add custom JS to returned HTML content
$customJS = "
<script>
/* Here I am writing a sample jQuery to hide the navigation menu
You can write your own jQuery for this content
*/
//Hide Navigation bar
jQuery(\".navbar.navbar-default\").hide();
</script>";
//Append Custom JS with HTML
$html = $content . $customJS;
//Return customized HTML
return $html;
}
For say i have a Site called example.com on which iframe is embedded of domain iframe.net, now i want to read the content of iframe and pass some parameter to display a textual message. Like Hi with username.
Now the problem is this able not able to make connection between the two , even am not able to get the innerHTML of iframe i used following approach
document.getElementById('myframe').contentWindow.document.body.innerHTML;
It throws error "Permission denied to access property"
Do anyone know how to read and write in cross domain platform
If you don't have control over the framed site, you cannot circumvent the cross-domain policy.
If you have control over both sites, you can use the postMessage method to transfer data across different domains. A very basic example:
// framed.htm:
window.onmessage = function(event) {
event.source.postMessage(document.body.innerHTML, event.origin);
};
// Main page:
window.onmessage = function(event) {
alert(event.data);
};
// Trigger:
// <iframe id="myframe" src="framed.htm"></iframe>
document.getElementById('myframe').contentWindow.postMessage('','*');
In Internet Explorer 8, events passed as a parameter may be null, that is why you need to access the event in a different manner:
In frame.html:
window.onmessage = function(event) {
var evt = event || window.event;
evt.source.postMessage('Message from iFrame', evt.origin);
};
On main.html:
window.onmessage = function(event) {
var evt = event || window.event;
alert(evt.data);
};
The event is triggered the same way as Rob W has presented:
document.getElementById('frameId').contentWindow.postMessage('message','*');
It can happen only if you have control for both sites
you can use postMessage
here's how you can apply it
first, you can add the below code to the site you want to retrieve data from
<script>
function test() {
document.getElementById('idMyIframe').contentWindow.postMessage("your message here", "*");
}
</script>
<iframe id="idMyIframe" onload="test()" src="http://example.otherdomaintosenddatato.com"></iframe>
the second step you can add this code below to the other domain you want to send a message or the data to it
window.addEventListener("message", function (message) {
if (message.origin == "http://firstdomain.com") {
console.log(message)
}
});
and please note you must add the test() function on the onload attribute of the iframe cause if the function runs before the iframe was loaded it will become useless and it won't send data
Cross Domain iframe elements access in React with (srcDoc):-
<iframe srcDoc={this.state.HTML} width='100%' id='iframetnd' onLoad={this.onclickinsideiframe}/>
onclickinsideiframe=()=>{
if(document.getElementById('iframetnd')!==null){
let iframe = document.getElementById('iframetnd');
let innerDocument = (iframe.contentDocument)
? iframe.contentDocument
: iframe.contentWindow.document;
let Obj = innerDocument.getElementsByClassName("navButtons")[0];
if(Obj !== undefined){
Obj.onclick = ()=>this.func();
}
}
}
func=()=>{
this.setState({page:2})
this.arrangeTest();
}
iFrame does not allow to access contents from Cross Domain platform. You can only access if your iFrame is using the same domain.
This solution works same as iFrame. I have created a PHP script that can get all the contents from the other website, and most important part is you can easily apply your custom jQuery to that external content. Please refer to the following script that can get all the contents from the other website and then you can apply your cusom jQuery/JS as well. This content can be used anywhere, inside any element or any page.
<div id='myframe'>
<?php
/*
Use below function to display final HTML inside this div
*/
//Display Frame
echo displayFrame();
?>
</div>
<?php
/*
Function to display frame from another domain
*/
function displayFrame()
{
$webUrl = 'http://[external-web-domain.com]/';
//Get HTML from the URL
$content = file_get_contents($webUrl);
//Add custom JS to returned HTML content
$customJS = "
<script>
/* Here I am writing a sample jQuery to hide the navigation menu
You can write your own jQuery for this content
*/
//Hide Navigation bar
jQuery(\".navbar.navbar-default\").hide();
</script>";
//Append Custom JS with HTML
$html = $content . $customJS;
//Return customized HTML
return $html;
}
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>
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.