【问题标题】:Invoking JavaScript code in an iframe from the parent page从父页面调用 iframe 中的 JavaScript 代码
【发布时间】:2010-09-20 01:37:30
【问题描述】:

基本上,我在页面中嵌入了一个iframe,而iframe 有一些我需要从父页面调用的JavaScript 例程。

现在相反的很简单,你只需要打电话给parent.functionName(),但不幸的是,我需要的正好相反。

请注意,我的问题不是更改iframe 的源URL,而是调用iframe 中定义的函数。

【问题讨论】:

  • 注意调试时你可以做诸如 window.location.href 或 parent.location.href 之类的事情来查看 iframe 的 url,如果你想验证你有一个对你的 iframe 的引用寻找。

标签: javascript html iframe


【解决方案1】:

假设你的 iFrame 的 id 是“targetFrame”,你要调用的函数是targetFunction()

document.getElementById('targetFrame').contentWindow.targetFunction();

您也可以使用window.frames 而不是document.getElementById 访问框架。

// this option does not work in most of latest versions of chrome and Firefox
window.frames[0].frameElement.contentWindow.targetFunction(); 

【讨论】:

  • 适用于 FF 7、Chrome 12、IE 8/9 和 Safari(不确定此版本)
  • 此解决方案不适用于我的小工具,这是我的代码 document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');" remote_iframe_0 是由 apache shindig 服务器以编程方式创建的,但 window.parent.document.getElementById('remote_iframe_0').contentWindow.my.create_element_gadg‌​et('verify_user');" 有效
  • @Dirty Henry “jQuery 函数”是什么意思? jQuery 是一个 JavaScript 库。
  • @JellicleCat 那是因为父页面和 iframe 有不同的主机,使其成为跨域框架,正如消息告诉你的那样。只要父页面和 iframe 的协议、域和端口匹配,一切都会正常。
  • 我添加了一个关于如何使用提到的 window.frames 方法的示例。
【解决方案2】:

这里有一些怪癖需要注意。

  1. HTMLIFrameElement.contentWindow 可能是更简单的方法,但它不是一个标准属性,并且一些浏览器不支持它,主要是旧版本的。这是因为 DOM Level 1 HTML 标准对 window 对象没有任何规定。

  2. 您也可以尝试HTMLIFrameElement.contentDocument.defaultView,一些较旧的浏览器允许但 IE 不允许。即便如此,该标准并没有明确说明您可以取回 window 对象,原因与 (1) 相同,但您可以在此处获取一些额外的浏览器版本。

    李>
  3. window.frames['name'] 返回窗口是最古老的,因此也是最可靠的接口。但是您必须使用name="..." 属性才能按名称获取帧,这有点难看/deprecated/transitional。 (id="..." 会更好,但 IE 不喜欢那样。)

  4. window.frames[number] 也很可靠,但知道正确的索引是诀窍。你可以摆脱这个,例如。如果您知道页面上只有一个 iframe。

  5. 完全有可能子 iframe 尚未加载,或者出现其他问题使其无法访问。您可能会发现反转通信流程更容易:也就是说,当子 iframe 完成加载并准备好被回调时,让子 iframe 通知其window.parent 脚本。通过将它自己的一个对象(例如回调函数)传递给父脚本,该父脚本可以直接与 iframe 中的脚本进行通信,而不必担心它与什么 HTMLIFrameElement 关联。

【讨论】:

  • 好东西!尤其是您的建议 5. 已证明非常有价值。在尝试从 Chrome 中的父级访问 iFrame 函数时,它解决了我很多头疼的问题。从 iFrame 中传递函数对象使其在 Chrome、FF 甚至 IE 中从 9 到 7 运行起来就像一个魅力,并且还可以很容易地检查函数是否已经加载。谢谢!
  • 3 号有效,但如果你像@le dorfier 在他的评论中那样做会更好。注意methode() 部分。
  • 还请注意,由于现代浏览器(经过测试的 FF29 和 Chrome34)的安全限制,函数调用的位置非常重要。仅从脚本标签或 $(document).ready() 调用该函数是行不通的。而是将调用放在正文的 onload 标记中,或者按照 elswhere 的建议(参见 stackoverflow.com/questions/921387/…)放在锚 do it 中。将函数调用作为回调传递给脚本通常也可以。
  • @chovy:不,请参阅 Vivek 对此的回答中的说明。
【解决方案3】:

iframe调用父JS函数是可能的,但只有当父和在iframe中加载的页面都来自同一个域,即abc.com,并且两者都使用相同的协议,即两者都在http://https://

在以下提到的情况下调用将失败:

  1. 父页面和 iframe 页面来自不同的域。
  2. 它们使用不同的协议,一种在 http:// 上,另一种在 https:// 上。

任何解决此限制的方法都非常不安全。

例如,假设我注册了域 superwinningcontest.com 并发送了指向人们电子邮件的链接。当他们加载主页时,我可以在其中隐藏几个iframes 并阅读他们的 Facebook 提要,查看最近的 Amazon 或 PayPal 交易,或者——如果他们使用的服务没有实现足够的安全性——转账出他们的账户。这就是 JavaScript 仅限于同域同协议的原因。

【讨论】:

  • 变通方法是使用美丽而危险的en.wikipedia.org/wiki/Cross-document_messaging
  • 有谁知道不同的子域是否会导致此失败?我在调试我认为与此相关的问题时遇到了困难 - iframe 正在调用父窗口函数,但调用没有发生。
  • 注意,不同的端口号也会失败。即 abc.com:80 != abc.com:8080
【解决方案4】:

在 IFRAME 中,将您的函数公开给窗口对象:

window.myFunction = function(args) {
   doStuff();
}

要从父页面访问,请使用:

var iframe = document.getElementById("iframeId");
iframe.contentWindow.myFunction(args);

【讨论】:

  • 很好的答案。不确定这里的约定。我有一个后续问题。如果我应该开始一个新问题,我会的。我有一个问题。我的页面的 iframe 是动态填充的。它包含一个按钮,其onclick() 事件仅调用window.print()。这完美地打印了iframe内容。但是当 iframe 的页面的公共函数调用window.print() 时,如果父页面调用了公共函数,则打印父页面的内容。我应该如何编写代码,以便在公共函数中对 window.print() 的调用与在 onclick 事件中的行为一样?
  • @Karl 你不想打电话给onclick。你想打电话给iframe.contentWindow.print()
  • 不幸的是,这不起作用。也许我应该开始一个新问题并记录我的代码?无论哪种方式,父页面内容也会打印。调用window.print()document.getElementById('myFrame').contentWindow.print();' and the pubic function printContent()`(不是oncick 事件处理程序)被这样调用:document.getElementById('myFrame').contentWindow.printContent();
  • @Karl - 是的,那么最好开始一个新问题。除非父窗口与 iframe 位于不同的域中。然后nothing you try will ever work.
  • 我在这里报告的似乎是一个IE问题(在IE8中测试)。当前版本的 Chrome 没有问题。但是,尚未测试其他浏览器。有兴趣的可以看看jsFiddle(我认为这是动态 iframe 加载和从父级调用公共函数的一个很好的例子。)
【解决方案5】:

如果 iFrame 的目标和包含的文档在不同的域中,之前发布的方法可能不起作用,但有一个解决方案:

例如,如果文档 A 包含一个包含文档 B 的 iframe 元素,并且文档 A 中的脚本在文档 B 的 Window 对象上调用 postMessage(),那么将在该对象上触发一个消息事件,标记为源自文档 A 的窗口。文档 A 中的脚本可能如下所示:

var o = document.getElementsByTagName('iframe')[0];
o.contentWindow.postMessage('Hello world', 'http://b.example.org/');

要为传入事件注册事件处理程序,脚本将使用 addEventListener()(或类似机制)。例如,文档 B 中的脚本可能如下所示:

window.addEventListener('message', receiver, false);
function receiver(e) {
  if (e.origin == 'http://example.com') {
    if (e.data == 'Hello world') {
      e.source.postMessage('Hello', e.origin);
    } else {
      alert(e.data);
    }
  }
}

此脚本首先检查域是预期域,然后查看消息,它要么显示给用户,要么通过将消息发送回最初发送消息的文档来响应。

通过http://dev.w3.org/html5/postmsg/#web-messaging

【讨论】:

  • easyXDM 为旧版浏览器提供对 PostMessage 的抽象(包括为顽固的恐龙提供 Flash (LocalConnection) 回退)。
  • 那太好了……在尝试了以上答案后,我终于得到了你的答案,谢谢!
【解决方案6】:

其中一些答案没有解决 CORS 问题,或者没有明确说明您将代码 sn-ps 放置在何处以使通信成为可能。

这是一个具体的例子。假设我想单击父页面上的一个按钮,并让它在 iframe 内执行某些操作。这是我将如何做到的。

parent_frame.html

<button id='parent_page_button' onclick='call_button_inside_frame()'></button>

function call_button_inside_frame() {
   document.getElementById('my_iframe').contentWindow.postMessage('foo','*');
}

iframe_page.html

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
    {
      if(event) {
        click_button_inside_frame();
    }
}

function click_button_inside_frame() {
   document.getElementById('frame_button').click();
}

要走另一个方向(单击 iframe 内的按钮以调用 iframe 外的方法),只需切换代码 sn-p 所在的位置,然后更改:

document.getElementById('my_iframe').contentWindow.postMessage('foo','*');

对此:

window.parent.postMessage('foo','*')

【讨论】:

  • parent.postMessage 方法在帧之间似乎仍然可靠。
【解决方案7】:

为了记录,我今天遇到了同样的问题,但这次页面嵌入在一个对象中,而不是 iframe 中(因为它是一个 XHTML 1.1 文档)。以下是它与对象的工作方式:

document
  .getElementById('targetFrame')
  .contentDocument
  .defaultView
  .targetFunction();

(抱歉,换行太丑了,一行都放不下)

【讨论】:

    【解决方案8】:

    IFRAME 应该在 frames[] 集合中。使用类似的东西

    frames['iframeid'].method();
    

    【讨论】:

    • 这个答案当然可以使用更多信息,因为肯定还有其他一些考虑因素。一方面,我假设开发人员需要将 method 设为 iframe 的 window 对象的属性。
    【解决方案9】:

    Quirksmode 有一个post on this

    由于该页面现已损坏,只能通过archive.org 访问,因此我在此处复制了它:

    IFrame

    在此页面上,我简要概述了从它们所在的页面访问 iframe。毫不奇怪,有一些浏览器注意事项。

    iframe 是一个内联框架,它包含一个具有自己的 URL 的完全独立的页面,但仍放置在另一个 HTML 页面中。这为网页设计提供了非常好的可能性。问题是访问 iframe,例如将新页面加载到其中。本页介绍了如何操作。

    框架还是对象?

    基本问题是 iframe 是被视为框架还是对象。

    • 正如Introduction to frames 页面所解释的,如果您使用框架,浏览器会为您创建框架层次结构(top.frames[1].frames[2] 等)。 iframe 是否适合此框架层次结构?
    • 或者浏览器是否将 iframe 视为另一个对象,一个恰好具有 src 属性的对象?在这种情况下,我们必须使用标准的DOM call(如document.getElementById('theiframe')) 来访问它。 通常,浏览器允许在“真实”(硬编码)iframe 上进行两种视图,但生成的 iframe 不能作为框架访问。

    名称属性

    最重要的规则是为您创建的任何 iframe 提供name 属性,即使您还使用了id

    <iframe src="iframe_page1.html"
        id="testiframe"
        name="testiframe"></iframe>
    

    大多数浏览器需要name 属性来使 iframe 成为框架层次结构的一部分。某些浏览器(尤其是 Mozilla)需要 id 才能使 iframe 作为对象访问。通过将这两个属性分配给 iframe,您可以保持选项打开。但name 远比id 重要。

    访问

    要么将 iframe 作为对象访问并更改其 src,要么将 iframe 作为框架访问并更改其 location.href

    document.getElementById('iframe_id').src = 'newpage.html'; 框架['iframe_name'].location.href = 'newpage.html'; 框架语法稍微好一点,因为 Opera 6 支持它,但不支持对象语法。

    访问 iframe

    因此,为了获得完整的跨浏览器体验,您应该为 iframe 命名并使用

    frames['testiframe'].location.href
    

    语法。据我所知,这总是有效的。

    访问文档

    只要您使用name 属性,访问 iframe 内的文档非常简单。要计算 iframe 中文档中的链接数,请执行 frames['testiframe'].document.links.length.

    生成的 iframe

    但是,当您通过W3C DOM 生成 iframe 时,iframe 不会立即输入到frames 数组中,并且frames['testiframe'].location.href 语法不会立即起作用。在 iframe 出现在数组中之前,浏览器需要一点时间,这段时间没有脚本可以运行。

    document.getElementById('testiframe').src 语法适用于所有情况。

    链接的target 属性也不适用于生成的 iframe,但在 Opera 中除外,即使我为生成的 iframe 同时提供了nameid

    缺少target 支持意味着您必须使用 JavaScript 来更改生成的 iframe 的内容,但是由于您首先需要 JavaScript 来生成它,所以我认为这不是什么大问题.

    iframe 中的文本大小

    一个奇怪的 Explorer 6 独有的错误:

    当您通过“查看”菜单更改文本大小时,iframe 中的文本大小会正确更改。但是,该浏览器不会更改原始文本中的换行符,因此部分文本可能会变得不可见,或者可能会出现换行符,而该行仍然可以容纳另一个单词。

    【讨论】:

    【解决方案10】:

    我找到了一个非常优雅的解决方案。

    正如您所说,执行位于父文档上的代码相当容易。这就是我的代码的基础,正好相反。

    当我的 iframe 加载时,我调用位于父文档上的函数,将一个对位于 iframe 文档中的本地函数的引用作为参数传递。 父文档现在可以通过此引用直接访问 iframe 的功能。

    示例:

    在父级上:

    function tunnel(fn) {
        fn();
    }
    

    在 iframe 上:

    var myFunction = function() {
        alert("This work!");
    }
    
    parent.tunnel(myFunction);
    

    当 iframe 加载时,会调用 parent.tunnel(YourFunctionReference),执行参数中接收到的函数。

    就这么简单,无需处理来自各种浏览器的所有非标准方法。

    【讨论】:

    • 您好,您能否确认一下“这么简单,无需处理来自各种浏览器的所有非标准方法。”?
    • 我在各种项目中使用这种方法,似乎效果很好。我没有亲自测试过所有流通的浏览器,但我也从未收到过关于它的错误报告。
    • 像魅力一样工作,IE 11,Chrome ~50。
    • 猜这不是跨域的..是吗?
    • @TusharShukla 不幸的是,不是。
    【解决方案11】:

    继续JoelAnair's 答案:

    为了更健壮,使用如下:

    var el = document.getElementById('targetFrame');
    
    if(el.contentWindow)
    {
       el.contentWindow.targetFunction();
    }
    else if(el.contentDocument)
    {
       el.contentDocument.targetFunction();
    }
    

    像魅力一样工作:)

    【讨论】:

      【解决方案12】:

      如果您想从另一个函数(例如 shadowbox 或 lightbox)生成的 iframe 调用 Parent 上的 JavaScript 函数。

      您应该尝试使用window 对象并调用父函数:

      window.parent.targetFunction();
      

      【讨论】:

      • 我相信 OP 正试图走向另一个方向
      • 对于像我这样来寻找反向通信的人来说,你是一个节省生命和时间的人
      【解决方案13】:

      按照 Nitin Bansal 的回答

      为了更加稳健:

      function getIframeWindow(iframe_object) {
        var doc;
      
        if (iframe_object.contentWindow) {
          return iframe_object.contentWindow;
        }
      
        if (iframe_object.window) {
          return iframe_object.window;
        } 
      
        if (!doc && iframe_object.contentDocument) {
          doc = iframe_object.contentDocument;
        } 
      
        if (!doc && iframe_object.document) {
          doc = iframe_object.document;
        }
      
        if (doc && doc.defaultView) {
         return doc.defaultView;
        }
      
        if (doc && doc.parentWindow) {
          return doc.parentWindow;
        }
      
        return undefined;
      }
      

      ...
      var el = document.getElementById('targetFrame');
      
      var frame_win = getIframeWindow(el);
      
      if (frame_win) {
        frame_win.targetFunction();
        ...
      }
      ...
      

      【讨论】:

        【解决方案14】:
               $("#myframe").load(function() {
                    alert("loaded");
                });
        

        【讨论】:

          【解决方案15】:

          How to refresh parent page from page within iframe 将是同样的事情但更简单的方法。 只需调用父页面的函数来调用javascript函数来重新加载页面:

          window.location.reload();
          

          或者直接从 iframe 中的页面执行此操作:

          window.parent.location.reload();
          

          两者都有效。

          【讨论】:

            【解决方案16】:

            试试parent.myfunction()

            【讨论】:

            【解决方案17】:

            使用以下调用父页面中框架的功能

            parent.document.getElementById('frameid').contentWindow.somefunction()
            

            【讨论】:

              猜你喜欢
              • 2013-10-14
              • 2013-02-23
              • 2013-02-16
              • 1970-01-01
              • 1970-01-01
              • 2017-04-28
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多