由於同源策略的限制,Javascript存在跨域通訊的問題,典型的跨域問題有iframe與父級的通訊等。
常規的幾種解決方案:
(1) document.domain+iframe; (2) 動態建立script; (3) iframe+location.hash; (4) flash。
這裡不細說這幾種方法,記錄的是HTML5的window.postMessage。postMessage相容IE8+、Firefox、Opera、Safari、Chrome。
需要兩個異域的伺服器來做測試,當然也可以用本地和線上伺服器作為兩個異域的伺服器。假如使用phonegap開發,就可以將請求檔案安裝在用戶端,然後動態請求伺服器的資料處理,獲得並顯示資料。這樣就可以用任意Web開發語言及方法來開發phonegap App所需的後台。
1. postMessage的用法
postMessage是HTML5為解決js跨域問題而引入的新的API,允許多個iframe/window跨域通訊。
假設有結構如下:
test.html
<section id="wrapper">
<header>
<h1>postMessage (跨域)</h1>
</header>
<article>
<form>
<p>
<label for="message">給iframe發一個資訊:</label>
<input type="text" name="message" value="son" id="message" />
<input type="submit" />
</p>
</form>
<h4>目標iframe傳來的資訊:</h4>
<p id="test">暫無資訊</p>
<iframe id="iframe"
src="http://xiebiji.com/works/postmessage/iframe.html"></iframe>
</article>
</section>
iframe.html
<strong>iframe指向xiebiji.com</strong>
<form>
<p>
<label for="message">給父視窗發個資訊:</label>
<input type="text" name="message" value="dad" id="message" />
<input type="submit" />
</p>
</form>
<p id="test">暫無資訊。</p>
下面是test.html裡的Javascript代碼(發送資料):
var win = document.getElementById("iframe").contentWindow;
document.querySelector('form').onsubmit=function(e){
win.postMessage(document.getElementById("message").value,"*");
if (e.preventDefault)
e.preventDefault();
e.returnValue = false;
}
關鍵代碼就一句:
win.postMessage(document.getElementById("message").value,"*");
postMessage是通訊對象的一個方法,所以向iframe通訊,就是iframe對象調用postMessage方法。postMessage有兩個參數,缺一不可。第一個參數是要傳遞的資料,第二個參數是允許通訊的域,“*”代表不對訪問的域進行判斷,可理解為允許所有域的通訊。
然後是iframe.html裡偵聽接收資料的代碼:
var parentwin = window.parent;
window.onmessage=function(e){
document.getElementById("test").innerHTML = e.origin + "say:" + e.data;
parentwin.postMessage('HI!你給我發了"<span>'+e.data+'"</span>。',"*");
};
很簡單,相信一看就懂了。e.data包含傳送過來的資料,e.origin指代源域。
然後iframe.html也給test.html發送回應的資料,test.html接收資料。代碼雷同,就不貼代碼了。
點此查看Demo(代碼素Joe哥給的)
2. Ajax跨域請求
基於以上的跨域通訊,只要將Ajax代碼放在iframe.html裡的onmessage處理函數裡頭,將test.html用postMessage傳過來的資料作為參數發送請求,再將返回的資料用postMessage傳給test.html。這樣就實現了跨域的Ajax請求。其實是很簡單的東西。
貼個範例程式碼吧,但跟以上的代碼無關。
(function(){
//擷取跨域資料
window.onmessage = function(e){
var url = "http://yangzebo.com/demo/noforget/test.php?msg=" + e.data;
//Ajax
var xhr = getXHR();
if(xhr){
xhr.open("GET",url,true);
xhr.onreadystatechange = changeHandle;
xhr.send(null);
}else{
alert("不支援Ajax");
}
function changeHandle(){//返回處理
if(xhr.readyState == 4){
var parentwin = window.parent;
parentwin.postMessage(xhr.responseText,"*");//跨域發送資料
}
}
function getXHR(){//擷取XMLHttpRequest
var xhr_temp;
if(window.XMLHttpRequest){
xhr_temp = new XMLHttpRequest();
}else if(window.ActiveXObject){
xhr_temp = new ActiveXObject("Microsoft.XMLHTTP");
}else{
xhr_temp = null;
}
return xhr_temp;
}
};
})();
然後給個不好看的Demo。
有興趣代碼請求啥的自個用開發人員工具看吧,“zebo男”是從資料庫讀出來的,“my msg”是sendAndReceive.html發給test.php的Ajax請求的參數,通過test.php和iframeforAjax.html回傳到sendAndReceive.html。
3. 提示
要擷取iframe的contentWindow才能調用postMessage。
postMessage一定要在iframe載入完成之後調用才可以正常運行。(這個耗了我好長時間)
最後要感謝Joe哥的提示和給我偷用伺服器來測試的hulin小妹妹。:D
[原帖]:http://yangzebo.com/blog/?p=208