如何在父窗口创建的iframe的onload处理程序中获取对iframe窗口对象的引用

时间:2013-04-15 15:05:48

标签: javascript html dom iframe

在粘贴任何代码之前,这是方案:

  1. 我有一个HTML文档,使用JavaScript
  2. 创建一个空的iframe
  3. JavaScript创建一个函数,并将对该函数的引用附加到iframe的文档对象(使用doc.open()来获取对文档的引用)
  4. 然后,该功能作为iframe文档的onload处理程序附加(通过将<body onload="...">写入iframe。
  5. 现在让我感到困惑的是onload处理程序中的全局(窗口)和文档对象(在它运行时)与通过脚本节点添加的JavaScript运行的相同对象不同。

    这是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>
    

    这里是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);
    

    说明:

    将这两个文件放入目录中,然后在浏览器中打开HTML(在最新的Chrome和Firefox中测试,两者都相同)。

    这是我得到的输出:

    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
    

    处理程序中的this对象应该是文档对象。它文档对象,但与它在其中运行的文档不是同一个文档对象(它也与父文档不同)。处理程序内的窗口对象也与在页面中加载的JavaScript中运行的窗口对象不同。

    最后我的问题是:

    有谁知道发生了什么,以及如何获得对实际窗口对象的引用,或者至少从同一个全局上下文声明和引用全局变量?

    脚注:

    此iframe没有跨域问题,因为它们位于同一个域中。如果某人设置了document.domain,则会出现问题,但在此示例代码中没有这样做。

1 个答案:

答案 0 :(得分:57)

您在父页面中声明了所有内容。因此,对windowdocument的引用是对父页面的引用。如果您想对iframe的内容进行操作,请使用iframe || iframe.contentWindow访问其window,并使用iframe.contentDocument || iframe.contentWindow.document访问其document

对于正在发生的事情有一句话,可能是“词汇范围”:What is lexical scope?

范围的唯一背景是这个。在您的示例中,方法的所有者为doc,即iframe的{​​{1}}。除此之外,在此函数中使用已知对象访问的任何内容都是父对象(如果未在函数中声明)。如果函数在不同的地方声明,那么它将是一个不同的故事,但它在父页面中声明。

这就是我写它的方式:

document

(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(); })(); windocw的别名不是必需的,它可能会因为对范围的误解而减少混淆。这样,它们就是参数,您必须引用它们才能访问d的内容。如果您想访问父级,您仍然使用iframewindow

我不确定将方法添加到document(在这种情况下为document)有什么影响,但在doc方法上设置_l方法可能更有意义{1}}。这样,事情可以在没有前缀的情况下运行......例如win