Proper way to get dimensions of a nested iframe - javascript

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

Related

Why doesn't iframe and/or innerHTML of a div always contain the source html of the iframe at time of generation?

Sometimes when this script executes on a page, especially if it is the first time the browser loads the page, the contents of the iframe will show the page at test.html but it will be an older page that is missing some changes that the actual test.html when loaded manually from the directory does contain. Every time setInterval() runs, the contents of the iframe will still be outdated.
If the whole page is refreshed, say by clicking the refresh button, the contents of the iframe will be updated and setInterval() will cause the iframe to show the current test.html. However, it seems like given enough time setInterval() will sometimes stop loading the current contents of test.html.
I suspect the answer may have something to do with Why is it suggested to avoid .innerHTML? or Whats wrong here, why is innerHTML not working? but I am an extreme novice and do not fully understand the limitations of innerHTML.
<script>
// Loads into a div an iframe that itself loads a page from the server.
document.getElementById("divy").innerHTML="<iframe width='600' height='800' id='cat' src='test.html'></iframe>";
iframe = document.getElementById('cat');
//Scrolls iframe to bottom.
iframe.onload = function () {
iframe.contentWindow.scrollTo(0,iframe.contentWindow.document.body.scrollHeight);
console.log(iframe.contentWindow.document.body.scrollHeight);
}
refresh_rate = 3000
// Every refresh_rate miliseconds, replaces the html inside the div with a new iframe so any changes to the page test.html are now shown.
setInterval(function(){
var element = document.getElementById('cat');
element.parentNode.removeChild(element);
document.getElementById("divy").innerHTML="<iframe width='600' height='800' id='cat' src='test.html'></iframe>";
iframe = document.getElementById('cat');
var x = document.getElementsByTagName('*');
console.log(x)
iframe.onload = function () {
iframe.contentWindow.scrollTo(0,iframe.contentWindow.document.body.scrollHeight);
console.log(iframe.contentWindow.document.body.scrollHeight);
}
}, refresh_rate);
</script>
Your test page is being cached somewhere, you need to bust the cache each time the page reloads. I've added a basic cache buster to a tidied up version of your code.
However, it would be much simpler to just update the src attribute of the iframe, rather than pull out the whole element and reinsert it every three seconds.
<script>
function loadCat(cacheBuster){
document.getElementById("divy").innerHTML="<iframe width='600' height='800' id='cat' src='test.html?'+cacheBuster></iframe>";
var iframe = document.getElementById('cat');
//Scrolls iframe to bottom.
iframe.onload = function () {
iframe.contentWindow.scrollTo(0,iframe.contentWindow.document.body.scrollHeight);
console.log('iFrame height: ', iframe.contentWindow.document.body.scrollHeight);
}
}
function removeCat(){
var element = document.getElementById('cat');
element.parentNode.removeChild(element);
}
var refresh_rate = 3000;
var counter = 0;
loadCat(counter);
// Every refresh_rate miliseconds, replaces the html inside the div with a new iframe so any changes to the page test.html are now shown.
setInterval(function(){
removeCat()
loadCat(++counter);
}, refresh_rate);
</script>

2 window.parent.onscroll calls on page do not work

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.

Change iframe src through js

So coming straight to the problem, i am writing a js which should change the src of iframe if screen resolution changes
<script language="javascript">
function newframe()
{
var w = screen.width;
var srchframe = document.getElementById("proj");
var targetURL1 = "http://wms.indianpropertynetwork.com/exchange/frames/clients.featured_projects.asp?cnt=5&wd=880&prj=1";
var targetURL2 = "http://lpm.indianpropertynetwork.com/exchange/frames/clients.featured_projects.asp?cnt=4&wd=668&prj=1";
if (srchframe.src != targetURL1 && w > 1300 )
{srchframe.src = targetURL1 }
else
{ srchframe.src = targetURL2 }
}
</script>
and calling this function in the following iframe
<iframe id="proj" onload="newframe();" marginwidth="0" marginheight="0" width="100%" height="200px" frameborder="0" scrolling="No" ></iframe>
The problem with script is it keeps on loading both the url in alternate order and won't stop loading.
You are using onload handler to start another load cycle, so continuously reloading is what your code seems to be intended to do.
Your code:
What to do on page load in iframe? Call newFrame().
This is in your code reading onload="newframe();"
newFrame() is setting frames src causing it to load another page.
Your if is assigning new URL to src of srchframe on any invocation of newframe(). And for it's loading URL 1 if current isn't URL 1 and URL 2 otherwise it is actually resulting in swapping both URLs.
Due to having switched pages in 2. another onload event will be triggered.
The essential problem is: by assigning URL to srchframe.src that frame's page gets replaced to load another one. So don't adjust the src property of iframe unless it is actually intended to load another page this way.
Basically you should try different approach. Take it as described initially:
should change the src of iframe if screen resolution changes
So you shouldn't bind to onload event of iframe, but to onresize event of outer window. Even though this isn't acting on changing screen resolution it is all better than your approach. Eventually try media queries as proposed in my answer to this post: https://stackoverflow.com/questions/23198520/website-adapting-to-different-monitor-sizes/23198630#23198630
Execute your function only once as onLoad is called on every change of src , for this purpose you can do with the help of a bool, below is the code you can use:
HTML:
<script language="javascript">
var newFrameCalled = false;
function newframe()
{
if(newFrameCalled)
return;
var w = screen.width;
var srchframe = document.getElementById("proj");
var targetURL1 = "http://wms.indianpropertynetwork.com/exchange/frames/clients.featured_projects.asp?cnt=5&wd=880&prj=1";
var targetURL2 = "http://lpm.indianpropertynetwork.com/exchange/frames/clients.featured_projects.asp?cnt=4&wd=668&prj=1";
if (srchframe.src != targetURL1 && w > 1300 )
{srchframe.src = targetURL1 }
else
{ srchframe.src = targetURL2 }
newFrameCalled = true;
}
</script>
Hope this helps.

Showing iFrame only after its source content has been completely loaded

I have a iFrame on my page thats display style is none. I have a javascript function to set the source and then set the display to block. The problem is that the iframe shows up before the content of it is loaded and thus I get a flickering effect. It goes white first and then displays the content. So I need to set the source, and when done loading all content of its source, only set its display style.
CSS & Javascript
.ShowMe{display:block;}
function(url)
{
document.getElementById('myIFrame').src = url;
document.getElementById('myIFrame').className = ShowMe;
}
It's simple as that:
<iframe onload="this.style.opacity=1;" style="opacity:0;border:none;
I would suggest you try the following:
<script type="javascript">
var iframe = document.createElement("myIFrame");
iframe.src = url;
if (navigator.userAgent.indexOf("MSIE") > -1 && !window.opera){
iframe.onreadystatechange = function(){
if (iframe.readyState == "complete"){
//not sure if your code works but it is below for reference
document.getElementById('myIFrame').class = ShowMe;
//or this which will work
//document.getElementById("myIFrame").className = "ShowMe";
}
};
}
else
{
iframe.onload = function(){
//not sure if your code works but it is below for reference
document.getElementById('myIFrame').class = ShowMe;
//or this which will work
//document.getElementById("myIFrame").className = "ShowMe";
};
}
</script>
Based on the code found here.
You could do this within the iframe:
window.onload = function () {
window.frameElement.className = 'ShowMe'; // 'ShowMe' or what ever you have in ShowMe variable.
}
Since you've tagged your question with [jquery], I assume you have executed the code within $(document).ready(). It is fired when the DOM is ready, i.e. it uses native DOMContentLoaded event (if available). window.onload is fired, when all resources on the page are ready.

Resize iframe on content refresh or update

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

Categories