I have an iframe that I would like to resize to match its contents whenever the contents auto refreshes (every few minutes) or when the user interacts with it. Although the contents are in the same domain, it would be difficult for me to modify the contents. Ideally, the iframe would just self adjust to the size of its contents.
Current code that resizes only once:
<iframe id="ganglia-frame" src="ganglia.url" width="100%" height="500%">
blah not supported blah
</iframe>
<script language="Javascript">
function setIframeHeight(iframe) {
if (iframe) {
var iframeWin = iframe.contentWindow ||
iframe.contentDocument.parentWindow;
if (iframeWin.document.body) {
iframe.height =
iframeWin.document.documentElement.scrollHeight ||
iframeWin.document.body.scrollHeight;
}
}
}
$(window).load(function () {
setIframeHeight(document.getElementById('ganglia-frame'));
});
</script>
Related question: Adjust width height of iframe to fit with content in it
What you need to do is to set a timer and check the iFrame size after a few moments. You may have to check several times as not all browsers return the correct size immediately.
This method works only on same-domain iFrames.
Bind a function to the iFrame onload event and when it executes check the height of the iFrame. If no height is found schedule another check in a few moments.
var iFrameSizeCount = 0;
var onloadFunction = function(event){
var contentHeight = document.getElementById('iFrameId').contentWindow.document.body.offsetHeight;
if(contentHeight == 0){
// Schedule a recheck in a few moments
iFrameSizeCount++; // we keep a count of how many times we loop just in case
if(iFrameSizeCount < 10){ // after a while we have to stop checking and call it a fail
setTimeout(function(){ onloadFunction(event); }, 200);
return false;
}
else {
contentHeight = 100; // eventually if the check fails, default to a fixed height. You could possibly turn scrolling to auto/yes here to give the iFrame scrollbars.
}
}
contentHeight += 30; // add some extra padding (some browsers give a height that's slightly too short)
document.getElementById('iFrameId').style.height = contentHeight + 'px';
}
Then bind the event to the onload event of your iFrame (however you want to):
The "scrolling=auto" is useful, just in case the sizing fails at least they have scrollbars. The onload event fires if the iFrame reloads, so is useful if they've clicked a link inside it.
I have had good luck with this jQuery plugin - https://github.com/davidjbradshaw/iframe-resizer
Related
I have a javascript in the head tag of a domain that is not mine that is looking for DIVs on the page to measure the height and width. The names of the DIVs are in an array. Each div has an undefined size. The size of the div will grow based on an iframe that loads from a different domain. The div may not always be the same size as the iframe so the div is not a reliable element to pull the size of. Therefore, I must measure the iframe. However, my problem is that my code loads in the head and sometimes loads before the div's size is defined which means the iframe isn't ready yet. I then do a setTimeout to check if the div's height and width is greater than 1. However, I notice that after the div and iframe content visually appears on the screen, there is a delay of a few seconds before the console logs the size of the iframe. I need the dimensions as soon as the iframe content loads. How can I make this code more efficient?
Head Tag of Page
var oDv = ["div-0", "div-1", "div-2"];
<script src="mydomain.js"></script>
Body of Page
<div id="div-0">
<script src="gets-an-iframe-from-some-other-domain.js"></script>
mydomain.js (aka my script)
window.addEventListener('load', pageFullyLoaded, false);
function pageFullyLoaded(e) {
var index;
for (index = 0; index < oDv.length; index++){
measure(oDv[index]);
}
}
function measure(div){
var divElement = document.getElementById(div);
if(divElement === null){
return;
}
var iframeElement = document.getElementById(div).getElementsByTagName('iframe')[0];
var iframeDimensions = window.getComputedStyle(iframeElement, null);
var iframeHeight = parseInt(iframeDimensions.getPropertyValue("height"));
var iframeWidth = parseInt(iframeDimensions.getPropertyValue("width"));
if((iframeHeight || iframeWidth) == 1){
timer();
}
else{
console.log(iframeHeight+" "+iframeWidth);
}
}
function timer(){
var T = setInterval(function(){
if((iframeHeight || iframeWidth) == 1){
iframeHeight = parseInt(iframeDimensions.getPropertyValue("height"));
iframeWidth = parseInt(iframeDimensions.getPropertyValue("width"));
}
else{
clearInterval(T);
}
}, 100);
UPDATE 1
I think I need to work around the window.addEventListener('load', pageFullyLoaded, false); because sometimes the first DIV loads while the rest of the page content is still loading. And sometimes the iframe loads before the page content is done. Therefore the top DIV/iframe are ready to be measured but my code hasn't started because it is still waiting for the the entire page to load. Also, I'd like to get away from using the setTimeout if possible.
UPDATE 2
I tried the following code but I get the following error: "Uncaught TypeError: Cannot read property 'getElementsByTagName' of null". I believe this is because when I check for this the DIV exists but the iFrame does not yet. I don't want to try adding an event listener to the window for the page to have loaded because I need the size as soon as possible.
function iframeReady(div){
console.log(div+' Start Function');
var iframe = document.getElementById(div).getElementsByTagName('iframe')[0];
iframe.onload = function() {
console.log('iFrame loaded');
var iframeDimensions = window.getComputedStyle(iframe, null);
var iframeHeight = parseInt(iframeDimensions.getPropertyValue("height"));
var iframeWidth = parseInt(iframeDimensions.getPropertyValue("width"));
console.log('iframeLoaded '+div+' iframe dimensions: '+iframeWidth+'x'+iframeHeight);
}
}
iframeReady('a-div-id');
In the above I see the following in the console:
a-div-id Start Function
Uncaught TypeError: Cannot read property 'getElementsByTagName' of null
I guess I'll just need to put the above into a settimeout loop to keep checking for the existence of the iframe first?
Try to use the iframe.onload event
var iframe = document.getElementByTagName("iframe")[0];
iframe.onload = function() {
console.log(iframe.getBoundingClientRect());
}
I have an HTML page with 2 iframes within it. I want each iframe to display the Y scroll offset of the parent window. One iframe works on page load, the other does not.
The parent HTML page just has the iframe embeds. Here is the code running in the iframe:
function run(){
document.write("<div id='scrollY' style='float:left;'></div><br>");
scrollY = 'no';
document.getElementById("scrollY").innerHTML = scrollY;
window.parent.onscroll = function(){
scrollY = window.parent.pageYOffset;
document.getElementById("scrollY").innerHTML = scrollY;
}
}
run();
The result on load and a scroll downwards will display something like:
iframe1 = 100
iframe2 = no
Can I only call window.parent.onscroll once? This doesn't seem right to me.
This is happening because in both frames, window.parent makes reference to the same window object, and you are setting the onscroll property/event on the same object, twice. This means that the second time around you set the window.parent.onscroll property, you're actually overwriting the first attempt to set the event, and you're overwriting it with the second frame's window.document object, which is why it only updates the contents of the elements in the second frame.
To overcome this issue, you can simply just use addEventListener instead of onscroll:
http://jsfiddle.net/x046o0p7/
function run() {
document.write("<div id='scrollY' style='float:left;'></div><br>");
scrollY = 'no';
document.getElementById("scrollY").innerHTML = scrollY;
window.parent.addEventListener('scroll', function() {
scrollY = window.parent.pageYOffset;
document.getElementById("scrollY").innerHTML = scrollY;
});
}
addEventListener allows you to set multiple events of the same type for the same element, while the .onstuff listeners don't.
I have some tabs on my page, and inside one tab of that page I have an iframe (#quiz_iframe).
This iframe is quiz frame, which is contained in a div (.quiz-div). After hitting a button (.start-quiz), quiz is loaded, and new div (.quiz-content) appears inside (.quiz-div).
In quiz content I have 20 another divs (.quiz-question), which are paginated, so after clicking a button next, prev, you can hide or show 5 next/prev divs.
Well the point is, I'd like to adjust iframe height to that content and I don't know how, after clicking .start-quiz.
Now I have a code which works fine, when the iframe is loaded for the first time (it adjust height to the laoded content)
function iframeLoaded() {
var iFrameID = document.getElementById('quiz_iframe');
if(iFrameID) {
iFrameID.height = "";
iFrameID.height = iFrameID.contentWindow.document.body.scrollHeight + "px";
}
}
<iframe onload="iframeLoaded()" id="quiz_iframe" src="'.$link.'" style="width:100%;"></iframe>'
How I can modify that code, to update iframe height on every .quiz-div height change? And yes, iframe is in the same domain :)
Thanks!
You can use a solution based on postMessage
You'll have to include some JavaScript in the iframe and on the parent page as well.
In your iframe :
window.addEventListener("load", function() {
parent.postMessage(getDocumentHeight(), "*");
});
In your parent page :
function receiveIframeHeight(frameWindow, height) {
$("iframe").each(function () {
if (this.contentWindow === frameWindow) this.height = height;
});
};
window.addEventListener("message", function (e) {
receiveIframeHeight(e.source, e.data);
});
You can now listen to the resize event and postMessage from your iframe to the parent page. But beware of infinite loop.
To see the whole code you should take a look at this Github repo iframe-autoheight-using-postmessage
In jQuery, you could use
$("myContainer .quiz_div").on("resize", function () {
$("#quiz_idrame").attr("height", your height calc goes here);
});
The "on" is a delegated event binding and thus if you add/remove items with the class of .quiz_div to the "myContainer" i.e. whatever contains the divs, it should bind.
This question already has answers here:
make iframe height dynamic based on content inside- JQUERY/Javascript
(23 answers)
Closed 8 years ago.
I have a group of links which load page contents to an iframe. That iframe has only height of the parent div and won't expand the page if needed, instead of it the iframe shows vertical scrollbar.
I don't want the scrollbar there, I want the iframe to "expand" (that is impossible) -> move contents from iframe each time it loads to the parent div.
For Example: Welcome.html has height 2000px, but the window has only 900px and the iframe shows the scrollbar. I want the contents of Welcome.html to extend the page.
Welcome
Another page
<div id="divMain"><iframe id="ifrMain" src="pages/welcome.html"></iframe></div>
I don't mind using jQuery. Something like "when the contents of iframe change, move them to the parent div."
Ensure that you are working on server and the iframe you loading is with same protocol. below is the code for your question
Javascript to be included in the head of the page
<script type="text/javascript">
function getDocHeight(doc) {
doc = doc || document;
// stackoverflow.com/questions/...
var body = doc.body, html = doc.documentElement;
var height = Math.max( body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight );
return height;
}
function setIframeHeight(id) {
var ifrm = document.getElementById(id);
console.log(ifrm)
var doc = ifrm.contentDocument? ifrm.contentDocument:
ifrm.contentWindow.document;
ifrm.style.visibility = 'hidden';
ifrm.style.height = "10px"; // reset to minimal height ...
// IE opt. for bing/msn needs a bit added or scrollbar appears
ifrm.style.height = getDocHeight( doc ) + 4 + "px";
ifrm.style.visibility = 'visible';
}
</script>
HTML
<iframe onload="setIframeHeight(this.id)" id="myframe" width="300" height="300" src="youpathtopagehere"></iframe>
You should call the functions on onload event only. otherwise it will not calculate height properly.
It depends on various factors.
Internet Explorer 8 and higher (and other modern browsers) have the postMessage API. In the content of the iFrame, you run parent.postMessage(JSON.stringify({action: 'resize', height: 2000}, "*"); and in the parent (your page) you run -
$(window).bind(
"message",
function (e)
{
var data = JSON.parse(e.originalEvent.data);
if (data.action === "resize")
{
// You must initialize iFrameElement first,
// or just get it here using document.getElementById or something.
iFrameElement.height = data.height;
}
});
If the iFrame shows a same origin content and you must support Internet Explorer 7, then give the iFrame a name attribute and the iFrame can call window.parent.document.getElementsByTagName(window.name)[0].height = 2000;.
It is more complicated if it is a cross origin content. Let me know if you need that (and you must control that cross origin content).
You should just do
function iframeLoaded() {
var iFrameID = document.getElementById('ifMain');
var wrapper = document.getElementById('wrapper');
if(iFrameID) {
$("#divMain").css("height", $(iFrameID.contentWindow.document).height()+"px");
}
}
That works
I needed to be able to load a particular page in an iframe on demand, so I used a simple wrapper:
function updateFrame(url) {
frames[0].location = url;
}
Then I was asked to load the page to a particular point, which was non-trivial, since the pages were not within my control and there weren't always <a name> anchors to rely on. So some poking around showed that IDs could be used as anchors.
That is to say, you can scroll to <div id = "somewhere-down-the-line"> with:
updateFrame("http://host/page#somewhere-down-the-line");
except this call also scrolls the entire viewport up so that the above <div> goes to the top and everything in the parent page above it scrolls out of view.
How do I modify updateFrame(url) so that it scrolls the page within the <iframe> but leaves the rest of the page as it is?
This hack worked for me on Firefox 20.0.1/Windows. Essentially, I load the page first, then jump to the target:
function updateFrame(url) {
if (url.indexOf('#') > -1) {
mainPage = url.split('#')[0];
frames[0].location = mainPage;
}
frames[0].location = url;
}
I would like to be able to use this in other browsers as well. I have been trying to get it to work in Chrome. Maybe I'll even try Internet Explorer...
If a hack is ok, and what you're looking for is cross-browser try using scrollTop to reset where you were.
E.g. if it is the body that scrolls
function updateFrame(url) {
//save where you were
var oldScroll = document.body.scrollTop;
//this moves our body!
frames[0].location = url;
//move it back
document.body.scrollTop = oldScroll;
}
Of course if it doesn't actually scrolls the entire viewport and instead modifies a parent div or something, the scrollTop property will be on that element too.
Let me know if this works, but screws up the scrolling on the frame, because I can modify this to account for a difference between the two scrollTops
You could try turning the bolts yourself by detecting the height of the element you want, and forcing the scrollTop of the frame.
function updateFrame(url) {
//get the parts
var parts = url.split('#');
//go to the url
frames[0].location = parts[0];
//if there was an anchor
var anchor;
if (parts.length > 0 && parts[1].length > 0) {
//may want to account for a[name="PARTS[1]"] too
anchor = frames[0].document.getElementById(parts[1]);
//set the scroll of it yourself, using some sort of library to get "fullTop"
frames[0].document.body.scrollTop = anchor.fullTop();
}
}
Where "fullTop" is equivalent to the distance between the top of the iframe, and the element.
Like jQuery's .offset() or YUI's getXY(el).[1]
What worked for me on Firefox 20.0.1/Windows. Essentially, I load the page first, then jump to the target:
function updateFrame(url) {
if (url.indexOf('#') > -1) {
mainPage = url.split('#')[0];
frames[0].location = mainPage;
}
frames[0].location = url;
}
On Chrome 28.0/Windows, calling updateFrame(url) followed by setting document.body.scrollTop = 0 (thanks to this answer) had the desired effect, though only in the console. I am still testing on other browsers; a more elegant solution is always appreciated :)
As mentioned in the question, though, I would like to be able to use this in other browsers as well. Maybe I'll even try Internet Explorer...