?
本文檔使用 php中文網(wǎng)手冊 發(fā)布
概述
iframe與主頁面的通信
參考鏈接
所謂“同域限制”指的是,出于安全考慮,瀏覽器只允許腳本與同樣協(xié)議、同樣端口、同樣域名的地址進(jìn)行通信。比如,www1.example.com頁面上面的腳本,只能與該域名(相同協(xié)議、相同端口)進(jìn)行通信,如果與www2.example.com通信,瀏覽器就會(huì)報(bào)錯(cuò)(不過可以設(shè)置兩者的document.domain為相同的值)。這是為了防止惡意腳本將用戶信息發(fā)往第三方網(wǎng)站。
window.postMessage方法就是用來在某種程度上,繞過同域限制,實(shí)現(xiàn)不同域名的窗口(包括iframe窗口)之間的通信。它的格式如下。
targetWindow.postMessage(message, targetURL[, transferObject]);
上面代碼的targetWindow是指向目標(biāo)窗口的變量,message是要發(fā)送的信息,targetURL是指定目標(biāo)窗口的網(wǎng)址,不符合該網(wǎng)址就不發(fā)送信息,transferObject則是跟隨信息一起發(fā)送的Transferable對象。
下面是一個(gè)postMessage方法的實(shí)例。假定當(dāng)前網(wǎng)頁彈出一個(gè)新窗口。
var popup = window.open(...popup details...); popup.postMessage("Hello World!", "http://example.org");
上面代碼的postMessage方法的第一個(gè)參數(shù)是實(shí)際發(fā)送的信息,第二個(gè)參數(shù)是指定發(fā)送對象的域名必須是example.org。如果對方窗口不是這個(gè)域名,信息不會(huì)發(fā)送出去。
然后,在當(dāng)前網(wǎng)頁上監(jiān)聽message事件。
window.addEventListener("message", receiveMessage, false); function receiveMessage(event) { if (event.origin !== "http://example.org") return; if (event.data == 'Hello World') { event.source.postMessage('Hello', event.origin); } else { console.log(event.data); } }
上面代碼指定message事件的回調(diào)函數(shù)為receiveMessage,一旦收到其他窗口發(fā)來的信息,receiveMessage函數(shù)就會(huì)被調(diào)用。receiveMessage函數(shù)接受一個(gè)event事件對象作為參數(shù),該對象的origin屬性表示信息的來源網(wǎng)址,如果該網(wǎng)址不符合要求,就立刻返回,不再進(jìn)行下一步處理。event.data屬性則包含了實(shí)際發(fā)送過來的信息,event.source屬性,指向當(dāng)前網(wǎng)頁發(fā)送信息的窗口對象。
最后,在popup窗口中部署下面的代碼。
// popup窗口 function receiveMessage(event) { event.source.postMessage("Nice to see you!", "*"); } window.addEventListener("message", receiveMessage, false);
上面代碼有幾個(gè)地方需要注意。首先,receiveMessage函數(shù)里面沒有過濾信息的來源,任意網(wǎng)址發(fā)來的信息都會(huì)被處理。其次,postMessage方法中指定的目標(biāo)窗口的網(wǎng)址是一個(gè)星號(hào),表示該信息可以向任意網(wǎng)址發(fā)送。通常來說,這兩種做法是不推薦的,因?yàn)椴粔虬踩?,可能?huì)被惡意利用。
所有瀏覽器都支持這個(gè)方法,但是IE 8和IE 9只允許postMessage方法與iFrame窗口通信,不能與新窗口通信。IE 10允許與新窗口通信,但是只能使用IE特有的MessageChannel對象。
iframe中的網(wǎng)頁,如果與主頁面來自同一個(gè)域,通過設(shè)置document.domain屬性,可以使用postMessage方法實(shí)現(xiàn)雙向通信。
下面是一個(gè)LocalStorage的例子。LocalStorage只能用同一個(gè)域名的網(wǎng)頁讀寫,但是如果iframe是主頁面的子域名,主頁面就可以通過postMessage方法,讀寫iframe網(wǎng)頁設(shè)置的LocalStorage數(shù)據(jù)。
iframe頁面的代碼如下。
document.domain = "domain.com"; window.onmessage = function(e) { if (e.origin !== "http://domain.com") { return; } var payload = JSON.parse(e.data); localStorage.setItem(payload.key, JSON.stringify(payload.data)); };
主頁面的代碼如下。
window.onload = function() { var win = document.getElementsByTagName('iframe')[0].contentWindow; var obj = { name: "Jack" }; win.postMessage(JSON.stringify({key: 'storage', data: obj}), "*"); };
上面的代碼已經(jīng)可以實(shí)現(xiàn),主頁面向iframe傳入數(shù)據(jù)。如果還想讀取或刪除數(shù)據(jù),可以進(jìn)一步加強(qiáng)代碼。
加強(qiáng)版的iframe代碼如下。
document.domain = "domain.com"; window.onmessage = function(e) { if (e.origin !== "http://domain.com") { return; } var payload = JSON.parse(e.data); switch(payload.method) { case 'set': localStorage.setItem(payload.key, JSON.stringify(payload.data)); break; case 'get': var parent = window.parent; var data = localStorage.getItem(payload.key); parent.postMessage(data, "*"); break; case 'remove': localStorage.removeItem(payload.key); break; } };
加強(qiáng)版的主頁面代碼如下。
window.onload = function() { var win = document.getElementsByTagName('iframe')[0].contentWindow; var obj = { name: "Jack" }; // 存入對象 win.postMessage(JSON.stringify({key: 'storage', method: "set", data: obj}), "*"); // 讀取以前存取的對象 win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*"); window.onmessage = function(e) { if (e.origin != "http://sub.domain.com") { return; } // 下面會(huì)輸出"Jack" console.log(JSON.parse(e.data).name); }; };
Mozilla Developer Network, Window.postMessage
Jakub Jankiewicz, Cross-Domain LocalStorage
David Baron, setTimeout with a shorter delay: 利用window.postMessage可以實(shí)現(xiàn)0毫秒觸發(fā)回調(diào)函數(shù)