2010年3月2日 星期二

[javascript]不使用地址輪詢的跨域iframe通信

引用

參考原文地址:

* Cross domain iframe communication without location polling
* Another Cross Domain iFrame Communication Technique
* Introducing CrossFrame, a Safe Communication Mechanism Across Documents and Across Domains

當我們需要在某個來自域 A 的頁面上包含來自另一個域 B 的頁面的時候,我們可以使用 iframe 元素。但基於瀏覽器的安全限制,父頁面中指向不同域 iframe 的 javascript 變量的屬性和方法基本是不可訪問,不可改寫的,但 iframe 的地址(src 屬性)和大小等樣式例外。所以在父頁面想和 iframe 頁面通信的時候,我們通常會使用消息接受者輪詢 iframe 的地址中的 hash 變化 (polling of location #hashchange ),消息發送者改變對應的 src 屬性或 location 中的 hash 來實現。

但這種輪詢有兩個缺點:

* 輪詢可能每秒5次左右,消息接受者不停的輪詢造成的額外CPU消耗
* 在 Safari 和 Opera 瀏覽器中,會因為 hash 變化造成虛假的瀏覽歷史(就是前進後退按鈕對應的那個)

Julien Lecomte 發表了一篇文章描述了一種改進這個過程的方法:在消息發送頁面創建和消息接收器同域的一個 iframe 代理頁面(proxy iframe),然後代理頁面會在 onload 事件觸發的時候從自身的 hash 中讀取到這個消息,然後通過 parent.frames['recieverFrame'] 把消息傳遞給和自身同域的接收者頁面,消息發送完畢後父級頁面可以移除這個臨時的代理 iframe。這樣,消息在 onload 的時候被傳遞,接收頁面就不需要不停的輪詢了。

看到這個改進後,Piers Lawson 覺得還有改進之處(他這個行為和他網站的域名倒是很一致:shouldersofgiants.co.uk,呵呵):消息的傳遞會有一個延遲,因為消息發送之後需要等待創建iframe、加載另一個域的一個頁面並且完成渲染觸發 onload 事件接收者頁面才能開始響應,如果這個加載過程很長的話,會是個不小的延遲。 Piers 提出了他的解決辦法:不使用臨時的會被移除的 iframe 代理,而是使用一個在頁面初始化的時候被創建的永久 iframe 代理,當消息被通過 src 屬性的方式傳遞給 iframe 代理的時候,消息發送者馬上改變 代理 iframe 的大小,例如width(注意前面提到的,src屬性和樣式是可以訪問和改寫的),而這個改變會觸發代理iframe 頁面中的 onresize 事件,代理 iframe 頁面只需要在 onresize 事件中獲取消息並且傳遞給本域的接收頁面就可以了,並且這個消息的傳遞過程是可逆(通過互相設置 proxy iframe)。

通過以上的方法,可以這樣簡單的向跨域的 iframe 通信:

1. function SendMessageToFrame(message) {
2. var elem = document.getElementById('innerFrameProxy');
3. elem.contentWindow.location = 'http://www.pierslawson.plus.com/Examples/CrossDomain/InnerFrameProxy.html#' + message;
4. elem.width = elem.width > 50 ? 50 : 100;
5. }

要看一個完整的例子,請翻牆後(iframe的域被牆了):

http://www.shouldersofgiants.co.uk/Examples/CrossDomain/ParentPage.html

要查看這個例子的全部源代碼:

* http://www.shouldersofgiants.co.uk/Examples/CrossDomain/ParentPage.html
* http://shouldersofgiants.co.uk/Examples/CrossDomain/ParentPageProxy.html
* http://www.pierslawson.plus.com/Examples/CrossDomain/InnerFrame.html
* http://www.pierslawson.plus.com/Examples/CrossDomain/InnerFrameProxy.html

最後,需要說明的是這個方法在 opera 下面是不適用的,opera的跨域限制更加嚴格。所以 Julien 其實不是很贊同大規模的使用這樣的 hack,因為這不是規範規定的,很可能你會發現現在支持的瀏覽器下個版本就不支持了。

【下列文章您可能也有興趣】

沒有留言: