ChatGPT解决这个技术问题 Extra ChatGPT

从父页面调用 iframe 中的 JavaScript 代码

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

现在相反的情况非常简单,因为您只需要调用 parent.functionName(),但不幸的是,我需要的恰恰相反。

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

请注意,在调试时,您可以执行 window.location.href 或 parent.location.href 之类的操作来查看 iframe 的 url,如果您想验证您是否引用了您正在寻找的 iframe。

C
Chris

假设您的 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 方法的示例。
D
Dominique Fortin

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

HTMLIFrameElement.contentWindow 可能是更简单的方法,但它不是一个标准属性,并且一些浏览器不支持它,主要是旧版本的。这是因为 DOM Level 1 HTML 标准没有关于 window 对象的内容。您还可以尝试 HTMLIFrameElement.contentDocument.defaultView,一些较旧的浏览器允许但 IE 不允许。即便如此,标准并没有明确说明您要取回 window 对象,原因与 (1) 相同,但如果您愿意,可以在此处获取一些额外的浏览器版本。 window.frames['name'] 返回窗口是最古老的,因此也是最可靠的接口。但是您必须使用 name="..." 属性才能按名称获取帧,这有点难看/不推荐使用/过渡性。 (id="..." 会更好,但 IE 不喜欢那样。) window.frames[number] 也非常可靠,但知道正确的索引是诀窍。你可以摆脱这个,例如。如果您知道页面上只有一个 iframe。完全有可能子 iframe 尚未加载,或者出现其他问题使其无法访问。您可能会发现反转通信流程更容易:也就是说,当子 iframe 完成加载并准备好回调时,让子 iframe 通知其 window.parent 脚本。通过将它自己的一个对象(例如回调函数)传递给父脚本,该父脚本可以直接与 iframe 中的脚本进行通信,而不必担心它与什么 HTMLIFrameElement 相关联。


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

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

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

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

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

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


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

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

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

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

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

很好的答案。不确定这里的约定。我有一个后续问题。如果我应该开始一个新问题,我会的。我有一个问题。我的页面的 iframe 是动态填充的。它包含一个按钮,其 onclick() 事件仅调用 window.print()。这完美地打印了 iframecontent。但是当 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 加载和从父级调用公共函数的一个很好的示例。)
1
19greg96

如果 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) 后备)。
太好了...在尝试了以上答案后,我终于得到了您的答案,谢谢!
C
Cybernetic

其中一些答案没有解决 CORS 问题,或者没有明确说明您放置代码片段的位置以使通信成为可能。

这是一个具体的例子。假设我想单击父页面上的一个按钮,并让它在 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 外的方法),只需切换代码片段所在的位置,然后更改:

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

对此:

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

parent.postMessage 方法在帧之间似乎仍然可靠。
T
Tamas Czinege

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

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

(对不起,丑陋的换行符,不适合单行)


R
Radu

Quirksmode 有一个 post on this

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

框架

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

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

框架还是物体?

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

正如框架页面简介中所述,如果您使用框架,浏览器会为您创建框架层次结构(top.frames[1].frames[2] 等)。 iframe 是否适合此框架层次结构?

或者浏览器是否将 iframe 视为另一个对象,一个恰好具有 src 属性的对象?在这种情况下,我们必须使用标准的 DOM 调用(如 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 中的文本大小会正确更改。但是,该浏览器不会更改原始文本中的换行符,因此部分文本可能会变得不可见,或者可能会出现换行符,而该行仍然可以容纳另一个单词。


该链接似乎已损坏。
更糟糕的是,我在 quirksmode 上找不到相关页面。
M
Michael Berkowski

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

frames['iframeid'].method();

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

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

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

当我的 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 不幸的是,不是,不是。
C
Community

继续 JoelAnair's 答案:

为了更稳健,请按如下方式使用:

var el = document.getElementById('targetFrame');

if(el.contentWindow)
{
   el.contentWindow.targetFunction();
}
else if(el.contentDocument)
{
   el.contentDocument.targetFunction();
}

像魅力一样工作:)


n
nircraft

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

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

window.parent.targetFunction();

我相信 OP 正试图走向另一个方向
对于像我这样寻求反向交流的人来说,您可以节省生命和时间
D
Dominique Fortin

遵循 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();
  ...
}
...

G
GeverGever
       $("#myframe").load(function() {
            alert("loaded");
        });

s
sangam

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

window.location.reload();

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

window.parent.location.reload();

两者都有效。


S
Saeid

试试parent.myfunction()


@Saeid OP想要从父窗口调用iframe中的函数,而不是相反。
V
Vaibs_Cool

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

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