An ad provider wants us to add some Javascript to our site that'll allow them to resize the iframe their ad is served into. I've been going through the code, and part of it is this loop:
var topIframes = top.document.getElementsByTagName('IFRAME');
for (var i = 0; i < topIframes.length; i++) {
if (topIframes[i].contentWindow === self) {
// found iframe that served the ad
topIframes[i].style.height = sz + 'px';
}
}
I can see it's grabbing all the iframes in the document and adjusting the height of one or more of them. But I can't figure out what the condition's doing.
I know contentWindow's the window inside an iframe, and looking at What's the difference between self and window? I see that "self" is a reference to the window object. But which window object? The parent window or the window inside the iframe? Is there even a window inside the iframe? Why check that the window inside an iframe is the window inside an iframe?
////////////////////////////////////////////
EDIT
At Snuffleapagus's request, here's the long version:
<script type="text/javascript">
// iframe shrink function that needs to be on the hosting page
rp_resize = function (sz) {
try {
var topIframes = top.document.getElementsByTagName('IFRAME');
for (var i = 0; i < topIframes.length; i++) {
if (topIframes[i].contentWindow === self) {
// found iframe that served the ad
topIframes[i].style.height = sz + 'px';
}
}
} catch (e) {
}
}
</script>
<script>
// this is the code that goes in the passback to initiate the function
try {
if (typeof(rp_mpu) === 'function') {
rp_resize(250);
}
} catch (e) {
}
</script>
<script language="JavaScript" type="text/javascript">
rp_account = '<account-id>';
rp_site = '<site-id>';
rp_zonesize = '<zone-id>-<size-id>';
rp_adtype = 'js';
rp_smartfile = 'http://<url>/..../revv_smart_file.html'; // this should be the URL path to the friendly iframe that needs resizing
</script>
<script type="text/javascript" src="http://ads.<url>.com/ad/<account-id>.js"></script>
////////////////////////////////////////////
EDIT
Here's a possible clue from the ad provider in answer to my question about the condition. Don't know how much use it is, as he's not a developer.
"The line of code you are looking at is trying to determine if it is the iFrame from which the function has been initiated so it can be resized accordingly."
From what I understand working with Javascript and how it can access iFrames, the provider is assuming that you have multiple iFrames on the page. Also, it assumes that the iFrame they are looking for does not have an ID to reference easily.
Based on this, after the frame with the ad content loads, at some point it will call rp_resize(250);. However, the function rp_resize does not know which of the iFrames on the page it was called from. The script loops through all the iFrames on the page until it finds the one that called the function. This is how it knows which frame to call.
Hopefully that makes sense and / or answers your question.
I think, self refers to the parent window. To check, type the following in your browser console and see the result :
self == window
.contentWindow will return null if the iframe hasn't completely loaded. It looks like the code is looping through iframes, checking if they are loaded, and if so, resizing them.
Edit: musefan is right; I worded it incorrectly.
Edit 2: Why check that the window inside an iframe is the window inside an iframe? It's null if it's not loaded yet; if it is loaded, it's a window.
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());
}
Inside my iframe i have a dial that spits out angle when the needle moves. Instead of getting both dial and needle both inside frame, is it possible to show the angle value somewhere else in my parent page outside the iframe. This is the code i have.
function showIframeContent(id) {
var iframe = document.getElementById(id);
try {
var doc = (iframe.contentDocument)? window.frames[clkwise1].document.getElementId("demo").value;
alert(doc.body.innerHTML);
}
catch(e) {
alert(e.message);
}
return false;
}
The standard and cross-browser way to access the content of an iframe is:
iframe.contentWindow.document
I would expect this to work in your case:
document.getElementById("clkwise1").contentWindow.document.getElementById("demo").value
(I'd need to see the page itself to confirm it - for example I assume "demo" is an input element. Also, note your typo in getElementById)
Live demo: http://jsfiddle.net/ZWC6v/
I'm fairly new to Javascript in general, and I cobbled together a small script from things found mostly on this site to try to get a small iframe to cycle through a bunch of links, which it does brilliantly. However, I also want it to stop cycling when the user clicks on the iframe or any of its contents.
Here is the code I have so far. There is only one iframe on the HTML page.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8">
<!--
var sites = [
"side/html5.html",
"side/silverlight.html",
"side/wordpress.html",
"side/mysql.html",
"side/php.html",
"side/css3.html"
];
var currentSite = sites.length;
var timerId;
var iframeDoc = $("iframe").contents().get(0);
$(document).ready(function ()
{
var $iframe = $("iframe").attr("src","side/html5.html");
timerId = setInterval(function()
{
(currentSite == 0) ? currentSite = sites.length - 1 : currentSite = currentSite -1;
$iframe.attr("src",sites[currentSite]);
}, 4000);
$(iframeDoc).bind('click', function()
{
clearInterval(timerId);
});
});
//-->
</script>
</head>
<body>
<sidebar>
<iframe name="sideframe" src="side/html5.html"></iframe>
</sidebar> ..etc..
Please help, I am trying to learn Javascript as fast as I can but as far as I can see, it should work, but it really doesn't.
Thanks for any help you can give me, it's really appreciated.
EDIT:
Okay, I've got a new script now, mostly based off of Elias' work, but it doesn't work either. I've pinned it down in Firebug and it's saying that the timerCallback.currentSite value IS updating properly, though I can't find the $iframe's src value to check for it explicitly. As far as I can tell, it is updating the variables properly, it's just not updating the iframe properly. Am I calling/setting the iframe correctly in this code? Also, is the linked in jquery library sufficient for my purposes? I'm a little lost of all these libraries to link to...
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript" charset="utf-8">
<!--
var sites =
[
"side/html5.html",
"side/silverlight.html",
"side/wordpress.html",
"side/mysql.html",
"side/php.html",
"side/css3.html"
];
var $iframe = $("iframe").attr("src","side/html5.html");
function timerCallback()
{
timerCallback.currentSite = (timerCallback.currentSite ? timerCallback.currentSite : sites.length) -1;
$iframe.attr("src",sites[timerCallback.currentSite]);
$($('iframe').contents().get('body')).ready(function()
{
$(this).unbind('click').bind('click',function()
{
var theWindow = (window.location !== window.parent.location ? window.parent : window);
theWindow.clearInterval(theWindow.timerId);
});
});
}
var timerId = setInterval(timerCallback, 4000);
//-->
</script>
If I were you, I'd play it safe. Since you say you're fairly new to JS, it might prove very informative.
function timerCallback()
{
timerCallback.currentSite = (timerCallback.currentSite ? timerCallback.currentSite : sites.length) -1;
$iframe.attr("src",sites[timerCallback.currentSite]);
}
var timerId = setInterval(timerCallback, 4000);
$($('iframe').contents().get('body')).unbind('click').bind('click', function()
{
var theWindow = (window.location !== window.parent.location ? window.parent : window);
theWindow.clearInterval(theWindow.timerId);
});
Now I must admit that this code is not tested, at all. Though I think it provides a couple of things to get you on your way:
1) the interval is set using a callback function, because it's just better for tons of reasons
1b) in that Callback, I took advantage of the fact that functions are objects, and created a static var, that is set to either the length of your sites array (when undefined or 0), in both cases 1 is substracted
2) jQuery's ,get() method returns a DOM element, not a jquery object, that's why I wrapped it in $(), a new jQ obj, giving you the methods you need.
3) since you're manipulating the dom inside the iFrame, it's best to unbind events you want to bind
4) inside the iFrame, you don't have direct access to the parent window, where your interval is.
You might want to read up on how to deal with iFrames, because they can be a bit of a faff from time to time
EDIT:
David Diez is right, easiest way around this is to incorporate the binding in the timeout function:
function timerCallback()
{
timerCallback.currentSide = ...//uncanged
//add this:
$($('iframe').contents().get('body')).ready(function()
{
$(this).unbind('click').bind('click',function()
{
//this needn't change
});
});
}
In theory, this should bind the click event to the body after it has been loaded
Edit2
I've been messing around a bit, you could keep your code, as is. just add a function:
function unsetInterval()
{
window.clearInterval(window.timerId);
}
and add this to your setInterval function:
$('#idOfIframe').load(function()
{
var parentWindow = window.parent;
$('body').on('click',function()
{
parentWindow.clearInterval();
});
});
this will get triggered as soon as the iFrame content is loaded, and bind the click event and unset the timer, like you wanted to
I think your code is not working because of this
var iframeDoc = $("iframe").contents().get(0);
This could be getting the header of the iFrame because you are saving the iframeDoc value to the first child of the iframe, the head tag, actually if you have more than 1 iframe in your window iframeDoc would be undefined because $("iframe") gets all the iframes of your document.
BTW your currentSite value condition is wrong, you asign the same value for both conditions
Now I give you an example:
<iframe id="myFrame" src="http://www.google.com/"></iframe>
and the script:
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
var sites = [
"site1",
"site2"
];
var myFrame = document.getElementsByTagName('iframe')[0];
var currentSite = myFrame.getAttribute('src');
var timerId;
var myFrameDoc = window.frames[0].document;
$(document).ready(function()
{
myFrame.setAttribute('src', 'side/html5.html');
timerId = setInterval(function()
{
//WRONGG
(currentSite == 0) ? currentSite = sites.length - 1 : currentSite = currentSite -1;
myFrame.setAttribute('src', sites[currentSite]);
$(myFrame).off("click").on("click", clearInterval(timerId));
}, 4000);
//Won't work because you are trying to get events from an inside of a iframe
//$(myFrameDoc).on("click", clearInterval(timerId));
//This may work
$(myFrameDoc).off("click").on("click", clearInterval(timerId));
});
</script>
When you try to track the events of an iframe you have to be carefull because an iframe contains a totally different document for javascript purprouses so basically you have to get inside the new document, unbind the events you need to use, and bind them again against your functionality, as #Elias points out. but be aware that you are changing constantly the src of your iframe, so if yu really need to do that you will have to separate the code that unbinds and binds again your clearInterval, and for that matter maybe $.on() could work for you.
EDIT: Calling to the iframe should work this way IF the src of the iframe is inside the same domain, with the same port and with the same protocol:
var myFrameDoc = window.frames[0].document;
I Added a new variable because we want to bind and unbind the click event to the iframe's document, not to the iframe, we use for that the window.frames collection property, but modern browsers throw an exception and denies acces if you try to access to a frame and you are not on the same domain with using the same port and the same protocol...
Additionaly the use of $.on() and $.off() instead of $.bind and $.unbind() is because the first ones are the new ones and despite we are not using it here, they are capable of watch constantly the DOM for new elements to bind if added; that could be useful to this case if this code still doesn't work. If that is the case you can still change this:
var myFrameDoc = window.frames[0].window;
and this:
$(myFrameDoc).off("click", "document").on("click", "document", clearInterval(timerId));
This will re-bind the event handler to new documents additions. Not tested but could work.
I have two pages, the parent and from this page I am using:
window.open('OrderDetailsFull.aspx?ObjectID=' + ObjectID[1] , "TableDetails","status=0 , toolbar=0 , location=no , menubar=0 , scrollbars=yes , height=600px , width=800px");
to open a new window and manipulate data over there.
When I finish what I am doing I need the parent page to refresh so I will get the new data data in it...
From what I know the method is:
top.opener.location.reload(true);
but for some reason, it is not working in IE8 or IE9...
As I am building an application and not a general web page. It will work on Windows OS with IE (as for now it is still the most common system...nothing to do about it) so I really need to solve this problem....
I couldn't find any new solution over the web for this problem, every one say it should work like that.....
Did anyone encounter this problem? and does any one knows how to solve it?
OK, FOLLOW UP question: when I do opener.location.reload(true); does it render the parent page all over again (as it sound) or not? If it does then I'm in a big problem, if not, then there must be a way to do that...
The problem is the I have an ajax call in the parent page and for some reason it stays in it's old values when I am using it, only when I reload the child window, the parent ajax shows the real results, some code follows...
This is in the document ready jQuery function of the opener page:
$('div[id^="divTable"]').hover(
function(e){
//קבלת זהות השולחן הנלחץ
ObjectID = $(this).attr('id').split('_');
$(this).css("cursor","pointer");
//AJAX הבאת נתוני רשומת ההזמנה מהשרת ב
var OrderDetails = $.ajax({
url:'AjaxActions/OrderDetails.aspx?ObjectID=' + ObjectID[1],
async:false
}).responseText;
//צף מעל שולחן כשעומדים עליו, ניתן לראות את פרטי הרשומה של אותו השולחן DIV
$(this).append($('<div style="position: absolute; top: 0; left: -150;">' + OrderDetails + '</div>'));
//וידוא שהשולחן עליו אנו עומדים יהיה העליון
$(this).css("z-index","10");
$(this).siblings().css("z-index","1");
},
//כשיוצאים מהשולחן DIVהעלמת ה
function () {
$(this).find('div:last').remove()
}
);
This is in one of the functions in the child window that should refresh the opener:
$('#ctrl_Print').click(
function()
{
alert($('#hidItem').val());
var Items = new Array();
Items = $('#hidItem').val().split(',');
for(var i=0;i<Items.length;i++)
{
alert(Items[i]);
}
opener.location.reload(true);
window.location = 'OrderDetailsFull.aspx?OrderID=' + OrderID + '&ObjectID=' + ObjectID + '&Print=' + Items;
window.close();
}
);
10x...
It looks like IE 8 and 9 have security restrictions on refreshing the opener.
I've run into this problem where reload() works in Chrome, but pukes in IE.
Try using window.location.replace(your url and params here).
You have to collect the url and params for replace(), but it gets around the IE error message.
Example:
In Joomla 2.5 parent window launches modal for users input, where we need to reload the parent window (view) in order to run code that uses modal input.
The modal fires a function in the parent window like;
function updateAddresses(runUpdate, itemID, closeModal){
if(closeModal == true ){
SqueezeBox.close();
}
if(runUpdate == true){
//location.reload();
var replaceURL = 'index.php?option=com_poecom&view=cart&ItemId='+itemID;
window.location.replace(replaceURL);
}
}
top is used to get the outermost document within the current physical window when you're dealing with framesets and/or iframes and is not related to window.open in any way, so you shouldn't use top unless there are frames or iframes within your pop-up page. The following will do:
opener.location.reload(true);
If you want to access the parent window (or frame), you should use parent, not top:
parent.location.reload(true);
When your page is a frame inside that window, add more parents to it:
parent.parent.location.reload(true);
Is there a way to reload an entire frameset using Javascript onload() event?
function logout() {
/* ... */
// reload entire frame
top.location.reload();
}
<body onload="logout()">
This cause all frames to reload but the URL of the frame where this was called didn't changed to the URL specified in the framset.
As I understood it, you want to reload each frame in a frameset using the original URL as stated in <frame src="...">.
This little function can do that (put it into the document holding the frameset):
this.reloadChildFrames = function()
{
var allFrames = document.getElementsByTagName("frame");
for (var i = 0; i < allFrames.length; i++)
{
var f = allFrames[i];
f.contentDocument.location = f.src;
}
}
You are then able to call that function from within any child frame:
top.reloadChildFrames()
Of course, this can only work when all frames come from the same origin.