本文來自:http://www.kuqin.com/webpagedesign/20090422/47458.html
javascript出於安全方面的考慮,是不允許跨域調用其他頁面的對象的。但在安全限制的同時也給注入iframe或是ajax應用上帶來了不少麻煩。沒有記錯的話前三屆D2論壇上每次都有人提這個東西,這裡把涉及到跨域的一些問題簡單地整理一下:
首先什麼是跨域,簡單地理解就是因為javascript同源策略的限制,a.com 網域名稱下的js無法操作b.com或是c.a.com網域名稱下的對象。更詳細的說明可以看下錶:
| URL |
說明 |
是否允許通訊 |
http://www.kuqin.com/lab/a.js
http://www.kuqin.com/script/b.js |
同一網域名稱下不同檔案夾 |
允許 |
http://www.kuqin.com/a.js
http://www.kuqin.com/b.js |
同一網域名稱下 |
允許 |
http://www.kuqin.com:8000/a.js
http://www.kuqin.com/b.js |
同一網域名稱,不同連接埠 |
不允許 |
http://www.kuqin.com/a.js
https://www.kuqin.com/b.js |
同一網域名稱,不同協議 |
不允許 |
http://www.kuqin.com/a.js
http://70.32.92.74/b.js |
網域名稱和網域名稱對應ip |
不允許 |
http://www.kuqin.com/a.js
http://script.kuqin.com/b.js |
主域相同,子域不同 |
不允許 |
http://www.ithao123.com/a.js
http://www.kuqin.com/b.js |
不同網域名稱 |
不允許 |
特別注意兩點:
第一,如果是協議和連接埠造成的跨域問題“前台”是無能為力的,
第二:在跨域問題上,域僅僅是通過URL的首部來識別而不會去嘗試判斷相同的ip地址對應著兩個域或兩個域是否在同一個ip上。
接下來簡單地總結一下在“前台”一般處理跨域的辦法,後台proxy這種方案牽涉到背景配置,這裡就不闡述了,有興趣的可以看看YAHOO的這篇文章:
JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls。
1、document.domain+iframe的設定
對於主域相同而子域不同的例子,可以通過設定document.domain的辦法來解決。具體的做法是可以在http://www.kuqin.com/a.html和http://script.kuqin.com/b.html兩個檔案中分別加上document.domain = ‘kuqin.com’;然後通過a.html檔案中建立一個iframe,去控制iframe的contentDocument,這樣兩個js檔案之間就可以“互動”了。當然這種辦法只能解決主域相同而次層網域不同的情況,如果你異想天開的把script.kuqin.com的domian設為alibaba.com那顯然是會報錯地!代碼如下:
www.kuqin.com上的a.html
123456789 |
document.domain = 'kuqin.com';var ifr = document.createElement('iframe');ifr.src = 'http://script.kuqin.com/b.html';ifr.style.display = 'none';document.body.appendChild(ifr);ifr.onload = function(){var x = ifr.contentDocument;alert(x.getElementsByTagName("h1")[0].childNodes[0].nodeValue);} |
script.kuqin.com上的b.html
1 |
document.domain = 'kuqin.com'; |
2、動態建立script
雖然瀏覽器預設禁止了跨域訪問,但並不禁止在頁面中引用其他域的JS檔案,並可以自由執行引入的JS檔案中的function,根據這一點,可以方便地通過建立script節點的方法來實現完全跨域的通訊。具體的做法可以參考yui的 Get Utility
這裡判斷script節點載入完畢還是蠻有意思的:ie只能通過script的readystatechange屬性,Safari 3.x以上支援的是script的load事件,而firefox和oprea則要通過onload來解決。另外這種辦法只能傳遞js類型的資料,不是很方便。以下是部分判斷script載入完畢的方法。
123456789101112131415161718192021222324252627282930313233 |
…… // ie支援script的readystatechange屬性// IE supports the readystatechange event for script and css nodesif (ua.ie) {n.onreadystatechange = function() {var rs = this.readyState;if ("loaded" === rs || "complete" === rs) {n.onreadystatechange = null;f(id, url);}}; …… // // Safari 3.x supports the load event for script nodes (DOM2) …… n.addEventListener("load", function() {f(id, url);}); …… // FireFox and Opera support onload (but not DOM2 in FF) handlers for// script nodes. Opera, but not FF, supports the onload event for link// nodes.} else {n.onload = function() {f(id, url);};} |
3、利用iframe和location.hash
這個辦法比較繞,但是可以解決完全跨域情況下的腳步置換問題。原理是利用location.hash來進行傳值。在url: http://kuqin.com#helloword中的‘#helloworld’就是location.hash,改變hash並不會導致頁面重新整理,所以可以利用hash值來進行資料傳遞,當然資料容量是有限的。假設網域名稱kuqin.com下的檔案cs1.html要和ithao123.com網域名稱下的cs2.html傳遞資訊,cs1.html首先建立自動建立一個隱藏的iframe,iframe的src指向ithao123.com網域名稱下的cs2.html頁面,這時的hash值可以做參數傳遞用。cs2.html響應請求後再將通過修改cs1.html的hash值來傳遞資料。(因為ie不允許修改parent.location.hash的值,所以要藉助於kuqin.com網域名稱下的一個代理iframe)。同時在cs1.html上加一個定時器,隔一段時間來判斷location.hash的值有沒有變化,一點有變化則擷取擷取hash值。代碼如下:
先是kuqin.com下的檔案cs1.html檔案:
12345678910111213141516 |
function startRequest(){var ifr = document.createElement('iframe');ifr.style.display = 'none';ifr.src = 'http://www.ithao123.com/lab/cscript/cs2.html#paramdo';document.body.appendChild(ifr);} function checkHash() {try {var data = location.hash ? location.hash.substring(1):'';if(console.log){console.log('Now the data is '+data);}}catch(e){};}setInterval(checkHash, 2000); |
ithao123.com網域名稱下的cs2.html:
1234567891011121314151617181920212223 |
(function(){//類比一個簡單的參數處理操作switch(location.hash){case '#paramdo':callBack();break;case '#paramset'://do something……break;} function callBack(){try { parent.location.hash = 'somedata'; } catch (e) { //ie的安全機制無法修改parent.location.hash,所以要利用一個中間在ithao123域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = 'http://kuqin.com/test/cscript/cs3.html#somedata'; document.body.appendChild(ifrproxy); }}})(); |
kuqin.com下的網域名稱cs3.html
12 |
//因為parent.parent和自身屬於同一個域,所以ie下可以改變其location.hash的值parent.parent.location.hash = self.location.hash.substring(1); |
執行個體請點擊 hash實現完全跨域
當然這樣做也存在很多缺點,諸如資料直接暴露在了url中,資料容量和類型都有限等……
4、利用flash
這是從YUI3的IO組件中看到的辦法,具體可見:http://developer.yahoo.com/yui/3/io/
flash這個方案不是很明白,各位自己慢慢琢磨了,呵呵。你可以看在Adobe Developer Connection看到更多的跨域代理檔案規格:
ross-Domain Policy File Specifications.
HTTP Headers Blacklist.