基本上,我有一个iframe
嵌入在页面中,并iframe
有一些我需要从父页面调用的JavaScript例程.
现在相反的情况非常简单,因为你只需要打电话parent.functionName()
,但不幸的是,我需要完全相反.
请注意,我的问题是不会改变的源URL的iframe
,但是调用在定义的函数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();
这里有一些需要注意的怪癖.
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 window.parent
在完成加载后通知其脚本并准备好回调.通过将其自己的一个对象(例如回调函数)传递给父脚本,该父级可以直接与iframe中的脚本进行通信,而不必担心与之关联的HTMLIFrameElement.
可以调用父JS函数iframe
,但只有当父iframe
元素和加载的页面都来自同一个域,即abc.com时,两者都使用相同的协议,即两者都是on http://
或者https://
.
在下面提到的情况下,呼叫将失败:
父页面和iframe页面来自不同的域.
他们使用不同的协议,一个在http://上,另一个在https://上.
这种限制的任何解决方法都是非常不安全的.
例如,假设我注册了域名superwinningcontest.com并发送了人们电子邮件的链接.当他们加载主页时,我可以iframe
在那里隐藏几个并阅读他们的Facebook提要,查看最近的亚马逊或PayPal交易,或者 - 如果他们使用的服务没有实现足够的安全性 - 将资金从他们的账户.这就是为什么JavaScript仅限于同域和同一协议.
在IFRAME中,将您的函数公开给window对象:
window.myFunction = function(args) { doStuff(); }
要从父页面进行访问,请使用以下命令:
var iframe = document.getElementById("iframeId"); iframe.contentWindow.myFunction(args);
如果iFrame和包含文档位于不同的域上,则先前发布的方法可能不起作用,但有一个解决方案:
例如,如果文档A包含包含文档B的iframe元素,并且文档A中的脚本在文档B的Window对象上调用postMessage(),则将在该对象上触发消息事件,标记为源自Window of 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
仅仅是为了记录,我今天遇到了同样的问题,但这次页面嵌入了一个对象,而不是iframe(因为它是一个XHTML 1.1文档).以下是它如何与对象一起使用:
document .getElementById('targetFrame') .contentDocument .defaultView .targetFunction();
(抱歉丑陋的换行符,不适合单行)
Quirksmode有一个帖子.
由于页面现在已经破了,只能通过archive.org访问,我在这里复制了它:
的IFrame
在这个页面上,我简要概述了从他们所在的页面访问iframe.毫不奇怪,有一些浏览器考虑因素.
iframe是一个内联框架,一个框架虽然包含一个完全独立的页面,但有一个自己的URL,但仍然放在另一个HTML页面中.这为网页设计提供了非常好的可能性.问题是访问iframe,例如将新页面加载到其中.本页介绍了如何操作.
框架还是物体?
根本问题是iframe是被视为一个框架还是一个对象.
正如框架页面简介中所述,如果使用框架,浏览器会为您创建框架层次结构(top.frames[1].frames[2]
等等).iframe是否适合此框架层次结构?
或者浏览器是否将iframe视为另一个对象,一个恰好具有src属性的对象?在这种情况下,我们必须使用标准的DOM调用(比如document.getElementById('theiframe'))
访问它.一般来说,浏览器允许在'真实'(硬编码)iframe上的两个视图,但生成的iframe不能作为帧访问.
NAME属性
最重要的规则是给你创建name
属性的任何iframe ,即使你也使用了id
.
大多数浏览器需要该name
属性来构成帧层次结构的iframe部分.某些浏览器(特别是Mozilla)需要id
将iframe作为对象进行访问.通过将这两个属性分配给iframe,您可以打开选项.但name
远比重要id
.
访问
您可以将iframe作为对象访问并更改它,src
或者您将iframe作为一个框架进行访问并更改它location.href
.
document.getElementById('iframe_id').src ='newpage.html'; frames ['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
除了Opera之外,链接的属性对生成的iframe都不起作用,即使我将生成的iframe都给了a name
和a id
.
缺乏target
支持意味着您必须使用JavaScript来更改生成的iframe的内容,但是因为您首先需要JavaScript来生成它,所以我不认为这是一个很大的问题.
iframe中的文字大小
一个好奇的Explorer 6唯一的bug:
通过"视图"菜单更改文本大小时,iframe中的文本大小会正确更改.但是,此浏览器不会更改原始文本中的换行符,因此文本的一部分可能会变为不可见,或者当行仍然可以保留另一个单词时可能会出现换行符.
IFRAME应该在frames[]
收藏中.使用类似的东西
frames['iframeid'].method();
我找到了一个很优雅的解决方案
正如您所说,执行位于父文档上的代码相当容易.这是我的代码的基础,恰恰相反.
当我的iframe加载时,我调用位于父文档上的函数,作为参数传递对位于iframe文档中的本地函数的引用.现在,父文档可以通过此引用直接访问iframe的功能.
例:
在父母身上:
function tunnel(fn) { fn(); }
关于iframe:
var myFunction = function() { alert("This work!"); } parent.tunnel(myFunction);
当iframe加载时,它将调用parent.tunnel(YourFunctionReference),它将执行参数中接收的函数.
这很简单,无需处理来自各种浏览器的所有非标准方法.
其中一些答案无法解决CORS问题,或者无法在放置代码段的地方变得显而易见,从而可以进行通信。
这是一个具体的例子。假设我想点击父页面上的按钮,然后在iframe中执行某些操作。这就是我要怎么做。
parent_frame.html
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','*')