子頁面iframe跨域執行父頁面定義的JS方法

問題需求:父頁面與子頁面iframe跨域嵌套,子頁面要觸發父頁面所定義的js方法、父子頁面的數據傳遞。javascript

下文中會用到一些文件:
父頁面: parent.html
嵌在父頁面的子iframe頁面:child.htmlhtml

同域時 iframe 調用父頁面的JS方法

在同域的狀況下,子iframe頁面能夠很方便地直接調用父頁面定義的JS方法:
window.parent.fn(); 或者 window.top.fn();
window.self: 當前窗口自身的引用
window.parent: 上一級父窗口的引用
window.top: 最頂層窗口的引用
當頁面中不存在 iframe 嵌套時,則 window.self, window.parent, window.top 三者均是當前窗口自身的引用。java

好比,parent.html 和 child.html 均在 a.com 的同一域名下,跨域

parent.html 代碼:函數

1
2
3
4
5
6
7
8
<iframe id="gameIframe" name="gameIframe" src="./game_iframe.html"></iframe>
<!-- 或者 -->
<iframe id="gameIframe" name="gameIframe" src="a.com/game_iframe.html"></iframe>
<script>
function sayHi () {
alert('hi!');
}
</script>

child.html 代碼:post

1
window.parent.sayHi(); //或者 top.sayHi();

當 <iframe> 的連接與父頁面不一樣域時,則子頁面的 iframe 不能調用父頁面定義的方法,會報錯;
如:parent.html 在 a.com 域名下,但子 iframe 的連接與 a.com 不一樣域:ui

1
2
<iframe id="gameIframe" name="gameIframe" src="b.com/game_iframe.html"></iframe>
<!-- 此時在game_iframe.html頁面調用父頁面定義的方法,會報跨域錯誤 -->

實際上,跨域直接調用其它頁面所定義的JS方法是作不到的。spa

postMessage 的發送與接收

Window.postMessage 是 HTML5 提供的一個跨域解決方案。基本的發送和接收使用以下:
發送:
otherWindow.postMessage(message, targetOrigin, [transfer]);
參數說明:
message: 將要發送到其餘 window的數據;
otherWindow:其餘窗口的一個引用,如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames;
targetOrigin: 經過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值能夠是字符串」*」(表示無限制)或者一個URI。code

接收:htm

1
2
3
4
5
6
7
window.addEventListener("message", function(event){
var data = event.data;
// 判斷域名
if(event.origin == 'http://192.168.1.237'){
//doSomething()
}
});

event 包含不少的信息,其中重要的幾個分別是:
event.data :傳遞過來的信息,也就是 postMessage 中發出的 message;
event.origin: 發送信息頁面的域名,包括協議和端口號。

跨域時 iframe 觸發父頁面的JS方法,數據雙向傳輸

a.com 域名下的父頁面 parent.html 定義了功能函數 sayHi();
父頁面 parent.html 中嵌套了子 iframe 頁面 child.html(在域名b.com域名下) ;
如今要實現:
1)在child.html中引發觸發、執行父頁面定義的 sayHi()方法。
2)在child.html中向父頁面請求獲取數據 uname 值。

基本思路:
parent.html 和 child.html 2個頁面分別設置 發送和接收,如圖:

1) child.html代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/javascript" src="sdk_child.js"></script>
<script type="text/javascript">
// 1)觸發父頁面定義的方法
window.SDK.sayHi({msg: 'hi'});
 
// 2)向父頁面請求獲取數據 uname
var uname = '';
window.SDK.getUname();
setTimeout(function(){
uname = window.SDK.uname;
//doSomething(uname);
}, 200);
// 備註:發送請求後,須要延時接收返回的數據
</script>

 

2) child.html 中引入的js文件 sdk_child.js 代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
;( function(){
var sdk = window.SDK || {};
sdk.uname = null;
 
//發送
sdk.getUname = function(){
window.top.postMessage({
action: "getUname"
},
"*")
};
sdk.sayHi = function(info){
window.top.postMessage({
action: "sayHi",
info: {
msg: info.msg
}
},
"*")
};
//接收
window.addEventListener("message", function(e){
var res = e;
var action = res.data.action;
var info = res.data.info;
//判斷域名
if(res.origin == 'a.com'){
switch (action) {
case 'getUname' :
sdk.uname = info;
break;
default :
return
}
}
});
//寫入window
window.SDK = sdk;
})();

3) parent.html 中引入的js文件 sdk_parent.js代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
;( function(){
var iframecont = document.getElementById('gameIframe').contentWindow;
var sdk ={
getUname: function(){
var info = Tool.pareUrl(location.href);
iframecont.postMessage({ action: 'getUname', info: 'zhangsan'}, 'b.com');
},
sayHi: function(info){
alert(info.msg);
},
};
 
//監聽接收
window.addEventListener("message", function(e){
var res = e;
var data = e.data;
var info = e.data.info;
if(true){
switch (data.action) {
case 'sayHi' :
sdk.sayHi(info);
break;
case 'getUname' :
sdk.getUname();
break;
default :
return
}
}
});
})();

本文的探索,主要應用在h5遊戲的JSSDK中。

相關文章
相關標籤/搜索