How to get a reference to an iframe's window object inside iframe's onload handler created from parent window
You're declaring everything in the parent page. So the references to window
and document
are to the parent page's. If you want to do stuff to the iframe
's, use iframe || iframe.contentWindow
to access its window
, and iframe.contentDocument || iframe.contentWindow.document
to access its document
.
There's a word for what's happening, possibly "lexical scope": What is lexical scope?
The only context of a scope is this. And in your example, the owner of the method is doc
, which is the iframe
's document
. Other than that, anything that's accessed in this function that uses known objects are the parent's (if not declared in the function). It would be a different story if the function were declared in a different place, but it's declared in the parent page.
This is how I would write it:
(function () {
var dom, win, doc, where, iframe;
iframe = document.createElement('iframe');
iframe.src = "javascript:false";
where = document.getElementsByTagName('script')[0];
where.parentNode.insertBefore(iframe, where);
win = iframe.contentWindow || iframe;
doc = iframe.contentDocument || iframe.contentWindow.document;
doc.open();
doc._l = (function (w, d) {
return function () {
w.vanishing_global = new Date().getTime();
var js = d.createElement("script");
js.src = 'test-vanishing-global.js?' + w.vanishing_global;
w.name = "foobar";
d.foobar = "foobar:" + Math.random();
d.foobar = "barfoo:" + Math.random();
d.body.appendChild(js);
};
})(win, doc);
doc.write('<body onload="document._l();"></body>');
doc.close();
})();
The aliasing of win
and doc
as w
and d
aren't necessary, it just might make it less confusing because of the misunderstanding of scopes. This way, they are parameters and you have to reference them to access the iframe
's stuff. If you want to access the parent's, you still use window
and document
.
I'm not sure what the implications are of adding methods to a document
(doc
in this case), but it might make more sense to set the _l
method on win
. That way, things can be run without a prefix...such as <body onload="_l();"></body>
bluesmoon
geek, speedfreak, paranoid, co-founder of lognormal, @bluesmoon on twitter
Updated on July 08, 2022Comments
-
bluesmoon almost 2 years
Before I paste any code, here's the scenario:
- I have an HTML document that creates an empty iframe using JavaScript
- The JavaScript creates a function and attaches a reference to that function to the iframe's document object (using
doc.open()
to get a reference to the document) - The function is then attached as an
onload
handler for the iframe's document (by writing<body onload="...">
into the iframe.
Now what has me stumped is that the global (window) and document objects inside the onload handler (while it's running) is different from the same objects run through JavaScript added via script nodes.
Here's the HTML:
<!doctype html> <html> <head> <script> (function(){ var dom,doc,where,iframe; iframe = document.createElement('iframe'); iframe.src="javascript:false"; where = document.getElementsByTagName('script')[0]; where.parentNode.insertBefore(iframe, where); doc = iframe.contentWindow.document; var _doc = document; doc.open()._l=function() { // the window object should be the one that doc is inside window.vanishing_global=new Date().getTime(); var js = this.createElement("script"); js.src = 'test-vanishing-global.js?' + window.vanishing_global; window.name="foobar"; this.foobar="foobar:" + Math.random(); document.foobar="barfoo:" + Math.random(); // `this` should be the document object, but it's not console.log("this == document: %s", this == document); console.log("this == doc: %s", this == doc); // the next two lines added based on @Ian's comment below console.log("_doc == document: %s", _doc == document); console.log("_doc == doc: %s", _doc == doc); console.log("name: " + window.name + "\n" + "window.vanishing_global: " + window.vanishing_global + "\ntypeof window.vanishing_global: " + typeof window.vanishing_global + "\ndocument.foobar: " + document.foobar); this.body.appendChild(js); }; doc.write('<body onload="document._l();"></body>'); doc.close(); })(); </script> </head> <body> </body> </html>
And here's
test-vanishing-global.js
:console.log("name: " + window.name + "\n" + "window.vanishing_global: " + window.vanishing_global + "\ntypeof window.vanishing_global: " + typeof window.vanishing_global + "\ndocument.foobar: " + document.foobar);
Instructions:
Put these two files into a directory, and open the HTML in a browser (tested in latest Chrome and Firefox, same result in both).
This is the output I get:
this == document: false this == doc: true _doc == document: true _doc == doc: false name: foobar window.vanishing_global: 1366037771608 typeof window.vanishing_global: number document.foobar: barfoo:0.9013048021588475 name: window.vanishing_global: undefined typeof window.vanishing_global: undefined document.foobar: foobar:0.5015988759696484
The
this
object inside the handler should be either the document object. It is a document object, but not the same document object as the document that it runs inside (it's also not the same as the parent document). The window object inside the handler is also not the same as the window object that runs in JavaScript loaded in the page.So finally my question:
Does anyone know what's going on, and how I can either get a reference to the actual window object, or at least declare and reference global variable from the same global context?
Footnotes:
There are no cross-domain issues with this iframe since they're on the same domain. There is an issue if someone sets
document.domain
, but that's not being done in this example code.-
Ian about 11 years
document
is the current page'sdocument
, not the iframe's. -
Ian about 11 yearsYou're declaring everything in the parent page. So the references to
window
anddocument
are to the parent page's. If you want to do stuff to the iframe's, useiframe.contentWindow || iframe
to access itswindow
andiframe.contentDocument || iframe.contentWindow.document
to access itsdocument
-
bluesmoon about 11 years@Ian, thanks. What's confusing me is that
_l
executes in the iframe's context, so shouldn'tdocument
andwindow
get values based on their run-time context? -
Ian about 11 yearsNope, the only context of a scope is
this
. And in your example, the owner of the method isdoc
, which is the iframe'sdocument
. Other than that, anything that's accessed in this function that uses known objects are the parent's. It would be a different story if the function were declared in a different place, but it's declared in the parent page.
-
yu yang Jian over 2 yearsif anyone need to Access elements of parent window from iframe, see stackoverflow.com/questions/7027799/…