[转]Cross Frame: 與不同網域的 Frame 做互動 ---- 转自:http://www.josephj.com/entry.php?id=338
為何要用不同網域的 Iframe?
通常較具規模的網站,會考慮這樣的安全性問題:當要使用到第三方所提供的 JavaScript 時,得放置在與頁面完全不同網域的 Iframe 中。例如在 Yahoo! 工作時,生活+ 與 UrMap合作的地圖就是放在另一個網域的 Iframe 中的。若不這樣子做,UrMap 網站的 JavaScript 可以隨時竄改 Yahoo! 網頁的內容、甚至危害到使用者。
用不同網域的 Iframe 來放置第三方程式碼的情形,簡單來說就是:「我不信任你的程式、但我需要你的程式成為我網頁中的一部分功能」(除了 Iframe,另外還有 ADSafe、Caja、Flash 等方式可以做到,但後三者都有一定的門檻。)
Same Origin Policy
將第三方網頁或程式碼、用不同網域的 Iframe 放到你的網頁上是一件非常簡單的事情 (<iframe src="xxx"></iframe> 人人都會用)。但問題在於當兩者網域不同時,JavaScript 是沒有辦法透過 parent, window.frames, top, opener 等方式存取的。這就是所謂 Same Origin Policy 的限制。
不能互動,就缺乏了彈性與應用的機會。例如剛剛提到 Yahoo! 生活+ 與 UrMap 的合作,生活+ 這邊會希望在頁面上點選了「餐廳」的連結時、就用 AJAX 取得伺服器的餐廳經緯度資訊、傳遞到放置地圖不同網域的 Iframe 中、Iframe 接到訊息後就以 UrMap API 動態更新網頁地圖上的座標點。而這樣的行為也會因 Same Origin Policy 無法直接達成。
Cross Document Messaging
WHATWG 提出了 Cross Document Messaging(也是 HTML 5 的 Web Messaging)來解決此一問題。簡單來說,你可以丟任何 String 到任何你網頁上的 Window 物件,但是該 Window 物件要不要處理此 String,就是它自己的判斷(會一併提供來源 Window 的 domain)。範例程式碼如下:
A 網頁 (http://yahoo.com/a.html) 放置了一個 name=urmap 的 iframe:
<iframe name="urmap" src="http://yahoomashup.com/urmap.html"></iframe>
A 網頁想要傳遞 Hello World 的訊息給此 Iframe:
<script>
/* 我是 yahoo.com */
window.frames['urmap'].postMessage("Hello World!", "*");
</script>
Iframe 裡的 B 網頁 (http://yahoomashup.com/urmap.html) 可以這樣接到資料:
/* 我是 yahoomashup.com */
window.onmessage = function (e) {
if (e.origin == "yahoo.com") { // 當來自 yahoo.com 時...
alert(e.data); // 顯示 "Hello World!"
}
}
好消息是目前大多數的主流瀏覽器支援此方式,包括 Firefox 3, Google Chrome, Opera, IE8, IE9 皆可。壞消息則是 IE6 與 IE7 沒有此功能。
Iframe In Iframe Hack
如果此主題有任何 A-Grade 瀏覽器無法使用,那也沒寫這篇文章的必要。只提供半套作法就遜掉了(這也是我對大多數 HTML 5 的新技術感到冷感的原因,IE[6-8] must die 也只是開發者肝苦下喊爽的產物,現實世界是不能不管 IE 的使用者的)其實早在 2007 年就有一些非正規 Cross Frame 的解法。這篇文章是目前解說最詳細的作法:Cross-Domain Communication with IFrames
其實原理相當簡單,就是當 A 網頁要傳遞訊息給 B 網頁的(在 Iframe 中)時,先動態產生一個與 B 網頁同網域的 Iframe,此動態 Iframe 內的 JavaScript 即可利用 window.frames 取得 B 網頁及做任何修改。另外還可透過修改生成 Iframe 中的 URL hash (#)、讓 JavaScript 可以依照不同參數做動作。這個作法的缺點是,你必須另外在接收端的網域放置一支 Proxy 的 HTML 檔、作為動態 Iframe 的網址。
YAHOO.util.CrossFrame 函式庫
雖然原理很簡單,但是要能夠有系統的溝通、寫程式就是一大挑戰了、中間的小細節其實是很多的。幸好 Yahoo! 的前端工程師 Julien Lecomte 用 YUI 2 寫了一個 CrossFrame Utility。
YAHOO.util.CrossFrame.send(
"http://www.y.com/proxy.html",
"frames['mashup']",
"message"
);
YAHOO.util.CrossFrame.onMessageEvent.subscribe(
function (type, args, obj) {
var message = args[0];
var domain = args[1];
// Do something with the incoming message...
}
);
看到了嗎?這邊其實就是用 YAHOO.util.CrossFrame.send() 取代 HTML5 的 window.postMessage()、用 YAHOO.util.CrossFrame.onMessageEvent 取代 window.onmessage 事件。單一 API 介面、所有瀏覽器都能用!
用 YUI 3 改寫為 Y.CrossFrame
因為公司內用的是 YUI 3,有時間的話當然希望改寫。另外就是 Julien 當時寫那篇文章的時間點是 2007 年,postMessage 只有 Opera 有實作,所以 Julien 對於 Opera 以外瀏覽器都採用 Iframe in Iframe 的作法,效能差上許多。也因此我改為以 postMessage 為主、Iframe in Iframe 為輔的作法較有效率。
Y.CrossFrame.postMessage(
"Hello World!", // 要傳遞的訊息
"frames['urmap']", // 要傳遞的視窗(請用 String)
{proxy:"http://yahoomashup.com/b.html"} // 不支援 postMessage 的瀏覽器會用到的 proxy 網址
);
Y.Global.on("crossframe:message", function (message, domain, url) {
alert(message);
});
目前測試 IE6, IE7, IE8, FF3, Chrome, Opera 都沒有問題(模擬器像是 IE Tester 無法使用)。希望對您有所幫助 :)