[javascript] How to get a reference to an iframe's window object inside iframe's onload handler created from parent window

Before I paste any code, here's the scenario:

  1. I have an HTML document that creates an empty iframe using JavaScript
  2. 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)
  3. 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.

This question is related to javascript html dom iframe

The answer is


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>


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to dom

How do you set the document title in React? How to find if element with specific id exists or not Cannot read property 'style' of undefined -- Uncaught Type Error adding text to an existing text element in javascript via DOM Violation Long running JavaScript task took xx ms How to get `DOM Element` in Angular 2? Angular2, what is the correct way to disable an anchor element? React.js: How to append a component on click? Detect click outside React component DOM element to corresponding vue.js component

Examples related to iframe

Getting all files in directory with ajax YouTube Autoplay not working Inserting the iframe into react component How to disable auto-play for local video in iframe iframe refuses to display iFrame onload JavaScript event YouTube iframe embed - full screen Change New Google Recaptcha (v2) Width load iframe in bootstrap modal Responsive iframe using Bootstrap