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

现在相反的是相当简单,因为你只需要调用parent.functionName(),但不幸的是,我需要的恰恰相反。

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


quirkmode在这方面有一篇文章。

由于这个页面现在已经坏了,只能通过archive.org访问,我在这里复制了它:

IFrames

在本页中,我简要介绍了如何从iframe所在的页面访问它们。毫无疑问,这里有一些浏览器方面的考虑。

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

框架还是物体?

最基本的问题是iframe是被视为一个框架还是一个对象。

As explained on the Introduction to frames pages, if you use frames the browser creates a frame hierarchy for you (top.frames[1].frames[2] and such). Does the iframe fit into this frame hierarchy? Or does the browser see an iframe as just another object, an object that happens to have a src property? In that case we have to use a standard DOM call (like document.getElementById('theiframe')) to access it. In general browsers allow both views on 'real' (hard-coded) iframes, but generated iframes cannot be accessed as frames.

名称属性

最重要的规则是给您创建的任何iframe一个name属性,即使您还使用id。

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

大多数浏览器需要name属性使iframe成为框架层次结构的一部分。一些浏览器(特别是Mozilla)需要id来将iframe作为对象访问。通过将这两个属性都分配给iframe,您可以保持选择的开放性。但名字远比身份重要。

访问

你可以将iframe作为对象访问并改变它的src,也可以将iframe作为框架访问并改变它的location.href。

. getelementbyid(“iframe_id”)。SRC = 'newpage.html'; 帧的iframe_name .location。Href = 'newpage.html'; 框架语法稍好一些,因为Opera 6支持它,但不支持对象语法。

访问iframe

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

frames['testiframe'].location.href

语法。据我所知,这总是管用的。

访问文档

如果使用name属性,则在iframe中访问文档非常简单。若要在iframe中计算文档中的链接数量,请执行 帧的testiframe .document.links.length。

生成的iframes

当你通过W3C DOM生成一个iframe时,iframe不会立即被输入到frames数组中,而frames[' testframe '].location。Href语法将不能立即工作。在iframe出现在数组中之前,浏览器需要一些时间,在这段时间内不可能运行任何脚本。

. getelementbyid(“testiframe”)。SRC语法在所有情况下都能正常工作。

链接的target属性也不能用于生成的iframe,除了在Opera中,尽管我给了我生成的iframe一个名称和id。

缺乏目标支持意味着您必须使用JavaScript来更改生成的iframe的内容,但由于您首先需要JavaScript来生成它,所以我不认为这是一个大问题。

iframe中的文本大小

Explorer 6特有的奇怪bug:

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


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

IFRAME应该在frames[]集合中。使用像这样的东西

frames['iframeid'].method();

在IFRAME中,让你的函数对窗口对象是公共的:

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

从父页面访问,使用这个:

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

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

HTMLIFrameElement.contentWindow is probably the easier way, but it's not quite a standard property and some browsers don't support it, mostly older ones. This is because the DOM Level 1 HTML standard has nothing to say about the window object. You can also try HTMLIFrameElement.contentDocument.defaultView, which a couple of older browsers allow but IE doesn't. Even so, the standard doesn't explicitly say that you get the window object back, for the same reason as (1), but you can pick up a few extra browser versions here if you care. window.frames['name'] returning the window is the oldest and hence most reliable interface. But you then have to use a name="..." attribute to be able to get a frame by name, which is slightly ugly/deprecated/transitional. (id="..." would be better but IE doesn't like that.) window.frames[number] is also very reliable, but knowing the right index is the trick. You can get away with this eg. if you know you only have the one iframe on the page. It is entirely possible the child iframe hasn't loaded yet, or something else went wrong to make it inaccessible. You may find it easier to reverse the flow of communications: that is, have the child iframe notify its window.parent script when it has finished loaded and is ready to be called back. By passing one of its own objects (eg. a callback function) to the parent script, that parent can then communicate directly with the script in the iframe without having to worry about what HTMLIFrameElement it is associated with.


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

为了记录,我今天遇到了同样的问题,但这次页面嵌入在对象中,而不是iframe(因为它是XHTML 1.1文档)。下面是它如何处理对象:

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

(对不起,很难看的换行,不适合单行)


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

在以下情况下调用将失败:

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

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

例如,假设我注册了域名superwinningcontest。例如,发送链接到人们的电子邮件。当他们加载主页时,我可以在那里隐藏一些iframe,阅读他们的Facebook动态,查看最近的亚马逊或贝宝交易,或者(如果他们使用的服务没有实现足够的安全性)从他们的账户转钱。这就是JavaScript仅限于同一域和同一协议的原因。


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

window.location.reload();

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

window.parent.location.reload();

这两个工作。


继续JoelAnair的回答:

为了获得更强的健壮性,使用如下方法:

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

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

像魅力一样工作:)


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

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


如果你想从另一个函数生成的iframe中调用父节点上的JavaScript函数,例如shadowbox或lightbox。

你应该尝试使用窗口对象和调用父函数:

window.parent.targetFunction();

试试parent.myfunction()


下面是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;
}

and

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

var frame_win = getIframeWindow(el);

if (frame_win) {
  frame_win.targetFunction();
  ...
}
...

我找到了一个很好的解决办法。

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

当我的iframe加载时,我调用父文档上的一个函数,将一个引用作为参数传递给位于iframe文档中的一个局部函数。 父文档现在可以通过这个引用直接访问iframe的函数。

例子:

在父节点上:

function tunnel(fn) {
    fn();
}

在iframe中:

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

parent.tunnel(myFunction);

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

这很简单,不需要处理来自各种浏览器的所有非标准方法。


使用following调用父页中帧的函数

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

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