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

Posted

技术标签:

【中文标题】如何在从父窗口创建的 iframe 的 onload 处理程序中获取对 iframe 窗口对象的引用【英文标题】:How to get a reference to an iframe's window object inside iframe's onload handler created from parent window 【发布时间】:2013-04-07 18:27:38 【问题描述】:

在我粘贴任何代码之前,这里是场景:

    我有一个使用 javascript 创建空 iframe 的 html 文档 JavaScript 创建一个函数并将对该函数的引用附加到 iframe 的文档对象(使用 doc.open() 获取对文档的引用) 然后将该函数附加为 iframe 文档的 onload 处理程序(通过将 <body onload="..."> 写入 iframe。

现在让我感到困惑的是,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 对象应该是文档对象。它一个文档对象,但与它在其中运行的文档不同(它也与父文档不同)。处理程序中的 window 对象也与页面中加载的 JavaScript 中运行的 window 对象不同。

所以最后我的问题:

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

脚注:

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

【问题讨论】:

document 是当前页面的document,而不是 iframe 的。 您在父页面中声明所有内容。所以对windowdocument 的引用指向父页面。如果你想对 iframe 做点什么,使用iframe.contentWindow || iframe 访问它的windowiframe.contentDocument || iframe.contentWindow.document 访问它的document @Ian,谢谢。令我困惑的是 _l 在 iframe 的上下文中执行,所以 documentwindow 不应该根据它们的运行时上下文获取值吗? 不,范围的唯一 contextthis。在您的示例中,该方法的所有者是doc,即iframe 的document。除此之外,在此函数中使用已知对象访问的任何内容都是父对象的。如果函数是在不同的地方声明的,但它是在父页面中声明的,那就是另一回事了。 【参考方案1】:

您在父页面中声明所有内容。所以对windowdocument 的引用指向父页面。如果你想对iframe 做点什么,使用iframe || iframe.contentWindow 访问它的window,使用iframe.contentDocument || iframe.contentWindow.document 访问它的document

有一个词表示正在发生的事情,可能是“词法范围”:What is lexical scope?

作用域的唯一上下文是 this。在您的示例中,该方法的所有者是doc,即iframedocument。除此之外,在此函数中使用已知对象访问的任何内容都是父对象(如果未在函数中声明)。如果函数是在不同的地方声明的,但它是在父页面中声明的,那就是另一回事了。

我会这样写:

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

windoc 的别名为 wd 不是必需的,它可能会因为对范围的误解而减少混淆。这样,它们是参数,您必须引用它们才能访问iframe 的内容。如果你想访问父母的,你仍然使用windowdocument

我不确定向document(在本例中为doc)添加方法的含义是什么,但在win 上设置_l 方法可能更有意义。这样,事情就可以在没有前缀的情况下运行......例如&lt;body onload="_l();"&gt;&lt;/body&gt;

【讨论】:

以上是关于如何在从父窗口创建的 iframe 的 onload 处理程序中获取对 iframe 窗口对象的引用的主要内容,如果未能解决你的问题,请参考以下文章

从父窗口访问 iframe 中的 DOM 附加函数

javascript, iframe, security - 尝试从父窗口访问 js 函数时权限被拒绝

从父文档/窗口调用IFrame Javascript函数

如何在从父视图过滤时重新渲染完整日历

如何从父页面调用 iframe 中的脚本?

如何从父文档中捕获 iframe 内部的刷新?