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


615

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

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

请注意,我的问题是不会改变的源URLiframe,但是调用在定义的函数iframe


42
您有关parent.fuctioName()的注释解决了我在iframe的父html中调用函数的问题。谢谢!
Cyber​​Fonic 2011年

1
请注意,在调试时,如果您想验证自己是否对要查找的iframe有引用,可以执行诸如window.location.href或parent.location.href之类的操作来查看iframe的网址。
AaronLS 2013年

参见@le dorfier的答案...对我来说效果很好。
ErickBest 2013年

Answers:


542

假设您的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(); 

3
可在FF 7,Chrome 12,IE 8/9和Safari(不确定该版本)中使用
Darcy

3
该解决方案不适用于我的小工具,这是我的代码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');"可以正常工作
J Bourne 2012年

3
@Dirty Henry“ jQuery函数”是什么意思?jQuery是一个JavaScript库。
Joel Anair

8
@JellicleCat这是因为父页面和iframe具有不同的主机,因此正如消息所告诉的那样,它成为跨源框架。只要父页面和iframe的协议,域和端口匹配,一切都可以正常工作。
Mark Amery 2014年

1
我添加了一个有关如何使用提及的window.frames方法的示例。
伊利亚·林恩

145

这里有一些怪癖要注意。

  1. HTMLIFrameElement.contentWindow可能是更简单的方法,但它不是标准属性,某些浏览器不支持它,大多数是较旧的浏览器。这是因为DOM级别1 HTML标准对此window对象无话可说。

  2. 您也可以尝试使用HTMLIFrameElement.contentDocument.defaultView,这是一些较旧的浏览器允许的,但IE不允许。即便如此,window由于与(1)相同的原因,该标准并未明确表示您将对象取回,但是如果您愿意的话,可以在此处选择其他一些浏览器版本。

  3. window.frames['name']返回窗口是最早的,因此也是最可靠的界面。但是您必须使用一个name="..."属性才能按名称获取框架,这有点难看/不推荐 /过渡。(id="..."会更好,但IE不喜欢那样。)

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

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


13
好东西!特别是您的建议5.已显示出非常宝贵的价值。尝试从Chrome的父级访问iFrame函数时,它解决了很多麻烦。从iFrame传递功能对象使它像在Chrome,FF甚至IE中从9降到7一样运行,并且非常容易检查功能是否已加载。谢谢!
Jpsy

第3个作品有效,但如果您像@le dorfier在其评论中所述的那样做,那就更好了。注意methode()零件。
ErickBest 2013年

另请注意,由于现代浏览器(经过测试的FF29和Chrome34)的安全性限制,放置函数调用的位置非常重要。仅从脚本标记或$(document).ready()调用该函数将不起作用。而是根据建议在其他地方将调用放在正文的onload标签内,或在锚点<a href="javascript:doit();">执行</a>中(请参见stackoverflow.com/questions/921387/…)。通常也可以将函数调用作为回调传递给脚本。
titusn

@chovy:不,请参见Vivek对此的回答。
bobince 2014年

66

可以从中调用父级JS函数iframe,但是仅当父级和加载到的页面iframe都来自同一个域(即abc.com)并且都使用相同的协议(即两者都在http://或上)时才可以https://

在以下情况下,呼叫将失败:

  1. 父页面和iframe页面来自不同的域。
  2. 他们使用的协议不同,一个协议位于http://,另一个协议位于https://。

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

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


6
解决方法是使用美丽而危险的en.wikipedia.org/wiki/Cross-document_messaging
Laramie

有谁知道其他子域是否会导致此失败?我在调试一个我认为与此相关的问题时遇到了困难-iframe正在调用父窗口函数,但是该调用没有发生。
杰克

1
注意,对于不同的端口号,它也会失败。即abc.com:80!= abc.com:8080
prasanthv

31

在IFRAME中,将函数公开给window对象:

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事件中的行为相同?
卡尔

1
@Karl您不想打电话onclick。您想打电话iframe.contentWindow.print()
Tomalak

不幸的是,这行不通。也许我应该开始一个新的Question并记录我的代码?无论哪种方式,父页面内容也会打印。document.getElementById('myFrame').contentWindow.print();' and the pubic function 这样调用的printContent()`(不是oncick事件处理程序)被这样调用window.print()document.getElementById('myFrame').contentWindow.printContent();
Karl

1
@Karl-是的,那么最好开始一个新问题。除非父窗口位于与iframe不同的域中。这样,您尝试的任何方法都将无法正常工作
Tomalak

我在这里报告的内容似乎是IE问题(在IE8中进行测试)。当前版本的Chrome没问题。尚未测试其他浏览器。对于那些感兴趣的人,请参见jsFiddle(我认为这是动态iframe加载并从父级调用公共函数的一个很好的例子。)
Karl

20

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

例如,如果文档A包含包含文档B的iframe元素,并且文档A中的脚本在文档B的Window对象上调用postMessage(),则将在该对象上触发消息事件,标记为源自Windows的Window文档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


1
easyXDM为较旧的浏览器提供了PostMessage的抽象(包括针对顽固的恐龙的Flash(LocalConnection)后备)。
JonnyReeves 2012年

1
太好了...在尝试了以上答案之后,终于得到了您的答案,谢谢!
vkGunasekaran

9

仅作记录,我今天遇到了同一问题,但是这次页面被嵌入对象而不是iframe(因为它是XHTML 1.1文档)。这是如何与对象一起使用的:

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

(很抱歉换行很丑,不能放在一行中)


8

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

frames['iframeid'].method();

这个答案当然可以使用更多信息,因为肯定还有其他注意事项。首先,我假设开发人员需要为methodiframe window对象创建属性。
matty 2015年

8

Quirksmode对此发表了一篇文章

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

内嵌框架

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

iframe是一个内联框架,该框架虽然包含一个完全独立的页面及其自己的URL,但仍放置在另一个HTML页面中。这在网页设计中提供了非常好的可能性。问题是要访问iframe,例如将新页面加载到其中。此页面说明了操作方法。

框架还是物体?

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

  • 框架简介页中所述,如果您使用框架,则浏览器会为您(top.frames[1].frames[2]等等)创建框架层次结构。iframe是否适合此框架层次结构?
  • 还是浏览器将iframe视为另一个对象,而该对象恰好具有src属性?在这种情况下,我们必须使用标准的DOM调用(例如document.getElementById('theiframe'))访问它)。一般而言,浏览器都允许在“真实”(硬编码)iframe上使用两个视图,但是生成的iframe不能作为框架进行访问。

NAME属性

最重要的规则是给您创建的所有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'; frame ['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 a name和anid

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

iframe中的文字大小

一个好奇的Explorer 6仅bug:

通过“视图”菜单更改文字大小时,iframe中的文字大小会正确更改。但是,此浏览器不会更改原始文本中的换行符,因此文本的一部分可能会变得不可见,或者可能在换行符仍包含另一个单词时发生换行符。


5
该链接似乎已断开。
zaphod

1
更糟糕的是,我在quirksmode上找不到相关页面。
stricjux


7

我找到了一个很好的解决方案。

如您所说,执行位于父文档中的代码非常容易。这就是我的代码的基础,反之亦然。

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

例:

在父项上:

function tunnel(fn) {
    fn();
}

在iframe上:

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

parent.tunnel(myFunction);

加载iframe时,它将调用parent.tunnel(YourFunctionReference),它将执行参数中接收的功能。

如此简单,而不必处理各种浏览器中的所有非标准方法。


嗨,您可以确认这一点“很简单,而不必处理各种浏览器中的所有非标准方法”。
Milche Patern,2015年

1
我在各种项目上使用此方法,似乎效果很好。我还没有亲自测试过所有浏览器的流通情况,但是我也从未收到过有关它的错误报告。
朱利安L

就像魅力一样,IE 11,Chrome〜50。
奥古斯塔斯

1
猜猜这不是跨域的。。
塔莎·舒克拉

@TusharShukla不幸的是,不是。
jeff-h

6

其中一些答案无法解决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','*')

4

继续JoelAnair的回答:

为了提高鲁棒性,请使用以下方法:

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

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

像魅力一样工作:)


3

跟随尼丁·班萨尔(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();
  ...
}
...

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

2

同样的事情,但是更简单的方法是如何从iframe中的页面刷新父页面。只需调用父页面的函数即可调用javascript函数来重新加载页面:

window.location.reload();

或直接在iframe中的页面上执行此操作:

window.parent.location.reload();

两者都可以。



1

如果要从其他函数(例如,shadowbox或lightbox)生成的iframe上调用Parent上的JavaScript函数。

您应该尝试利用windowobject并调用父函数:

window.parent.targetFunction();

我相信OP正在尝试朝另一个方向发展
CommandZ

-3

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

parent.document.getElementById('frameid').contentWindow.somefunction()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.