iframe里的js操作父级窗口的dom
iframe里的js要操作父级窗口的dom,必须搞懂几个对象:
parent是父窗口(如果窗口是顶级窗口,那么parent==self==top),
top是最顶级父窗口(有的窗口中套了好几层frameset或者iframe), self是当前窗口(等价window),
opener是用open方法打开当前窗口的那个窗口;
这样iframe里的js要操作父级窗口的dom可以通过parent,top这些对象来获取父窗口的window对象,例如:
parent.document.getElementById(“dom ID”);
parent,top还能调用父级窗口的的js方法,比如,getIFrameDOM(iID)是父级窗口的一个方法,那么iframe里可以使用parent.getIFrameDOM(“wIframeA”)来调用父级窗口的getIFrameDOM(iID)方法;
iframe父子互调
1. 父页面获取子页面元素:
格式:$("#iframe的ID").contents().find("#iframe中的控件ID").click();
实例:$("#ifm").contents().find("#iBtnOk").click(); // ifm 为 <iframe> 标签 id; iBtnOk 为子页面按钮 id
2. 父页面调用子页面方法:
格式:$("#iframe的ID")[0].contentWindow.iframe方法();
实例:$("#ifm")[0].contentWindow().iClick(); // ifm 为 <iframe> 标签 id; iClick为子页面 js 方法
3. 子页面获取父页面元素:
格式:$("#父页面元素id" , parent.document);
实例:$("#pBtnOk" , parent.document).click(); // pBtnOk 为父页面标签 id
对照表
1、js
父调子 | 子调父 | |
---|---|---|
元素 | window.frames[iframe的name属性值]; | ① window.parent.document.getElementById("父页面元素ID"); ②window.parent.document.getElementsByTagName("父页面元素标签名")[i]; ③window.parent.document.getElementsByClassName("父页面元素类名")[i]; |
方法 | ① document.getElementById("子页面元素ID").contentWindow.子页面方法名; ② document.getElementsByTagName("子页面元素标签名")[i].contentWindow.子页面方法名; ③ document.getElementsByClassName("子页面元素类名")[i].contentWindow.子页面方法名; | window.parent.父页面方法; |
2、jquery
父调子 | 子调父 | |
---|---|---|
元素 | $(iframe选择器).contents().find(iframe中元素选择器); | ① $(父页面元素选择器, window.parent.document); ② window.parent.$(父页面元素选择器)window.parent.父页面方法; |
方法 | $(iframe选择器)[0].contentWindow.子页面方法名; | window.parent.父页面方法; |
最近在项目中遇到了一个在客户端中的webview中嵌入iframe页面的问题,但是嵌入的iframe无法自己撑开,所以需要webview中给iframe一个高度。
webview的Bug
这就比较尴尬了,因为webview肯定不知道iframe的高度,所以需要iframe给传一个高度出来。
经过一番查找之后,知道了一种很方便的方法-postMessage方法。
先来看看他的语法:
otherWindow.postMessage(data, origin, [transfer]);
otherWindow
- 其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。这里即可以是父窗口,也可以子窗口,两者是可以相互传递数据的。
data
- 将要发送到其他window的数据。它将会被结构化克隆算法序列化(这个不懂是什么意思)。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。基本是什么参数都可以,字符串、数字、对象、数组,都OK。
origin
- 字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
transfer(可选)
- 是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。(不懂是什么意思,一般没有什么用吧,望大神指点)
父页面获取iframe页面信息:
demo我放在github上的:
demo地址:父页面获取iframe页面信息
父页面:
<body>
<p>我是父页面</p>
<p>接收到的iframe信息是:<span id="content"></span></p>
<iframe src="/btoa/b.html" frameborder="0" marginheight="0" marginwidth="0"></iframe>
<script>
// event 参数中有 data 属性,就是iframe页面发送过来的数据
window.addEventListener("message", function(event) {
// 把iframe页面发送过来的数据显示在父页面中
document.getElementById("content").innerHTML= event.data;
}, false);
</script>
</body>
iframe页面
<body>
<p>我是iframe页面</p>
<div>发送的信息是:<input id="iframe" type="text"><input id="sendBtn" type="button" value="发送"></div>
<script>
// 点击按钮后向父页面发送数据
document.getElementById('sendBtn').onclick = function() {
// window.parent代表父页面
window.parent.postMessage(document.getElementById("iframe").value, '*');
}
</script>
</body>
iframe页面获取父页面信息:
demo我放在github上的:
demo地址:iframe页面获取父页面信息
父页面:
<body>
<p>我是父页面</p>
<div>发送的信息是:<input id="iframe" type="text"><input id="sendBtn" type="button" value="发送"></div>
<iframe src="/atob/b.html" frameborder="0" marginheight="0" marginwidth="0"></iframe>
<script>
// 点击按钮后向iframe页面发送数据
document.getElementById('sendBtn').onclick = function() {
// window.frames[0]代表第一个iframe页面
window.frames[0].postMessage(document.getElementById("iframe").value, '*');
}
</script>
</body>
iframe页面
<body>
<p>我是iframe页面</p>
<p>接收到的父页面信息是:<span id="content"></span></p>
<script>
// event 参数中有 data 属性,就是父页面发送过来的数据
window.addEventListener("message", function(event) {
// 把父页面发送过来的数据显示在父页面中
document.getElementById("content").innerHTML= event.data;
}, false);
</script>
</body>
通过这两个简单的例子我们会发现postMessage的强大之处,有了postMessage,我们甚至可以在两个iframe之间传送数据,只需要一个共同的父元素做中间页。
需要注意的地方:
兼容性:
caniuse
通过图上可以看出,基本大部分的都能兼容的,但是需要注意一下这句话。
Internet Explorer 8 and 9, and Firefox versions 6.0 and below only support strings as postMessage's message.(ie 8和9,和Firefox 6.0及以下版本只支持字符串作为postMessage的消息。)
所以如果需要支持这些版本的需要注意data只能是字符串。
安全性:
在线上进行交互时,最好做好安全措施,对于postMessage,最好采用“双向安全机制”。发送方发送数据的时候,确认接受方的源(所以最好不要用*),而接受方监听到message事件后,也可以用event.origin判断是否来自于正确可靠的发送方。并且最好做一下传递数据的数据类型校验。如以下代码:
function checkMessage(event) {
// 只获取需要的域,并非所有都可以跨域
if (event.origin != "need domain") {
return false;
}
var data = event.data;
// 传输数据类型校验
if (typeof(data) !== 'object') {
return false;
}
// data 的type中包含xxx则为xxx需要字段。
return data.type === "xxx";
};
并且通过不同的type可以处理不同的数据,如以下代码:
switch (checkMessage(event)) {
case 'height':
$("#bet_"+e.data.id).css("height", (e.data.height+1) + "px");
break;
case 'jumpDown':
window.location.href = downUrl;
break;
default:
break;
}
4、完整代码
main.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>iframe+postMessage 跨域通信 主页面</title>
</head>
<body>
<h1>主页面</h1>
<iframe id="child" src="http://b.com/iframepage.html"></iframe>
<div>
<h2>主页面接收消息区域</h2>
<span id="message"></span>
</div>
</body>
<script>
window.onload = function(){
document.getElementById('child')
.contentWindow.postMessage("主页面消息",
"http://b.com/iframepage.html")
}
window.addEventListener('message', function(event){
document.getElementById('message').innerHTML = "收到"
+ event.origin + "消息:" + event.data;
}, false);
</script>
</html>
iframepage.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>iframe+postMessage跨域通信 子页面</title>
</head>
<body>
<h2>子页面</h2>
<div>
<h3>接收消息区域</h3>
<span id="message"></span>
</div>
</body>
<script>
window.addEventListener('message',function(event){
if(window.parent !== event.source){return}
console.log(event);
document.getElementById('message').innerHTML = "收到"
+ event.origin + "消息:" + event.data;
top.postMessage("子页面消息收到", 'http://a.com/main.html')
}, false);
</script>
</html>