轉(zhuǎn) http://ued.alipay.com/?p=707
關(guān)于跨域這個話題,很早就答應(yīng)過要分享,但是因?yàn)閼校恢蓖现钡紻2上有人談起了“完美跨域”。“跨域”應(yīng)該已經(jīng)算不上什么難題了,只是提起“完美”這兩個字,突然覺得有了新意,那就寫點(diǎn)什么吧,至少對自己有個交代嘛!跨域方案有很多,接下來一一枚舉,會給出demo,demo的宗旨是盡可能簡單的讓新手明白,各方案中跨域的原理,實(shí)際應(yīng)用請酌情修改。
方案一、剪貼板
原理:IE本身依附于windows平臺的特性為我們提供了一種基于iframe,利用內(nèi)存來“繞行”的方案,在這里我稱之為,本地存儲原理。
缺點(diǎn):不支持非IE瀏覽器,并且影響到用戶對剪貼板的操作,用戶體驗(yàn)非常不好,特別是在IE7下,受安全等級影響,會彈出提示框。
演示:[ 點(diǎn)此閱覽 ]
子頁面在子域:demo.ioldfish.cn下,在頁面中加入如下代碼獲取頁面高度并存入剪貼板。
- <script type="text/javascript">
- var ua = navigator.userAgent;
- if ((i = ua.indexOf("MSIE")) >= 0)
- {
- var iObjH = window.document.documentElement.scrollHeight;
- window.clipboardData.setData('text',String(iObjH));
- }
- </script>
主頁面在主域:www.ioldfish.cn下,在頁面中加入如下代碼,獲取剪貼板的值并賦值為子頁面所在的iframe的高度。
- <script type="text/javascript">
- window.onload = function()
- {
- var iObj =document.getElementById('iId');
- iObj.style.height=parseInt(window.clipboardData.getData('text'))+'px';
- }
- </script>
方案二、document.domain
原理:設(shè)置了document.domain,欺騙瀏覽器
缺點(diǎn):無法實(shí)現(xiàn)不同主域之間的通訊。并且當(dāng)在一個頁面中還包含有其它的iframe時(shí),會產(chǎn)生安全性異常,拒絕訪問。
演示:[ 點(diǎn)此閱覽 ]
主頁面在主域:www.ioldfish.cn下,子頁面在子域:demo.ioldfish.cn下,在兩個頁面的頭部都加入如下代碼:
- <script type="text/javascript">
- document.domain="ioldfish.cn";
- </script>
方案三、通過JS獲取hash值實(shí)現(xiàn)通訊
原理:hash可以實(shí)現(xiàn)跨域傳值實(shí)現(xiàn)跨域通訊。
缺點(diǎn):對于父窗口URL參數(shù)動態(tài)生成的,處理過程比較復(fù)雜一些。并且IE之外的瀏覽器遇到hash的改變會記錄歷史,影響瀏覽器的前進(jìn)后退功能,體驗(yàn)不佳。
演示:[ 點(diǎn)此閱覽 ]
子頁面在主域:www.lzdaily.com下,在頁面中添加如下代碼,因?yàn)閔ash是不受跨域限制的,所以可以將本頁面的高度順利的傳送給主頁面的hash。
- <script type="text/javascript">
- var hashH = document.documentElement.scrollHeight;
- var urlA = "http://www.ioldfish.cn/wp-content/demo/domain/hash/a2.html";
- parent.location.href= urlA+"#"+hashH;
- </script>
主頁面在主域:www.ioldfish.cn下,在頁面中添加如下代碼,首先取得子頁面?zhèn)鬟f過來的hash值,然后將hash值賦到子頁面所在的iframe的高度上。
- <script type="text/javascript">
- window.onload = function()
- {
- var iObj = document.getElementById('iId');
- if(location.hash)
- {
- iObj.style.height=location.hash.split("#")[1]+"px";
- }
- }
- </script>
方案四、傳hash值實(shí)現(xiàn)通訊改良版
原理:該方案通過“前端代理”的方式,實(shí)現(xiàn)hash的傳值,體驗(yàn)上比之方案三有了很大的提升。
缺點(diǎn):對于父窗口URL參數(shù)動態(tài)生成的,處理過程比較復(fù)雜一些。
演示:[ 點(diǎn)此閱覽 ]
子頁面在主域:www.lzdaily.com下,在頁面中添加如下代碼,首先在頁面里添加一個和主頁面同域的iframe[也可動態(tài)生成],他的作用就像是個跳板。C頁面中不需任何代碼,只要確保有個頁面就防止404錯誤就可以了!該方法通過修改iframe的name值同樣可以跨域傳值,只是比較”猥瑣“而已。
- <iframe id="iframeC" name="iframeC" src="http://www.ioldfish.cn/wp-content/demo/domain/hashbetter/c.html" frameborder="0" height="0"></iframe>
然后在頁面中加上如下代碼,利用頁面C的URL接收hash值,并負(fù)責(zé)把hash值傳給同域下的主頁面。
- <script type="text/javascript">
- hashH = document.documentElement.scrollHeight;
- urlC = "http://www.ioldfish.cn/wp-content/demo/domain/hashbetter/c.html";
- document.getElementById("iframeC").src=urlC+"#"+hashH;
- </script>
主頁面在主域:www.ioldfish.cn下,在頁面中添加如下代碼,獲取從C頁面中傳遞過來的hash值。這里應(yīng)用到一個技巧,就是直接從A頁面用frames["iId"].frames["iframeC"].location.hash,可以直接訪問并獲取C頁面的hash值。這樣一來,通過代理頁面?zhèn)鬟fhash值,比起方案三,大大提高了用戶體驗(yàn)。
- <script type="text/javascript">
- window.onload = function()
- {
- var iObj = document.getElementById('iId');
- iObjH = frames["iId"].frames["iframeC"].location.hash;
- iObj.style.height = iObjH.split("#")[1]+"px";
- }
- </script>
方案五、JSONP
原理:有點(diǎn)腳本注入的味道
缺點(diǎn):服務(wù)器端程序運(yùn)行比腳本早,跨域交互時(shí)無法捕獲前端頁面元素的相關(guān)數(shù)據(jù),比如自適應(yīng)高度。
演示:[ 點(diǎn)此閱覽 ]
主頁面在主域:www.ioldfish.cn下,在頁面中添加如下代碼,動態(tài)創(chuàng)建一個script,然后指定到子域的動態(tài)文件,在動態(tài)文件后面可以添加參數(shù),在這里我加了一個回調(diào)函數(shù),當(dāng)請求返回后,會運(yùn)行這個回調(diào)函數(shù)。
- <script type="text/javascript">
- function loadContent()
- {
- var scriptDom=document.createElement('script');
- var url = "http://www.lzdaily.com/domain/jsonp/Test.aspx?f=setDivContent'";
- scriptDom.src= url;
- document.body.appendChild(scriptDom);
- }
- function setDivContent(love)
- {
- var fishDiv = document.getElementById("oldFish");
- fishDiv.innerHTML = love;
- }
- </script>
子頁面在主域:www.lzdaily.com下,在頁面中添加如下代碼,首先先取得主頁面?zhèn)鬟^來的回調(diào)函數(shù)名,然后生成一段javascript代碼,以回調(diào)函數(shù)帶參數(shù)的形式返回主頁面,這樣就完成了跨域之間的通訊。由于服務(wù)器端程序執(zhí)行總是優(yōu)先于javascript代碼,所以基本上沒辦法獲取到子頁面中DOM的相關(guān)數(shù)據(jù),所以小白同學(xué),我可以很負(fù)責(zé)人的告訴你,想通過這種方法實(shí)現(xiàn)跨域自適應(yīng)高度是不可能的!
- <script language="C#" runat="server">
- void Page_Load(object sender, EventArgs e)
- {
- string f = Request.QueryString["f"];
- Response.Clear();
- Response.ContentType = "application/x-javascript";
- Response.Write(String.Format(@"{0}({1});", f,1122));
- Response.End();
- }
- </script>
方案六、window.name
原理:window.name本身并不能實(shí)現(xiàn)跨域,只是中間做了代理。
缺點(diǎn):獲取異域的前端頁面元素值有一定局限性,比如取自適應(yīng)高度的值。但是此方法在前端頁面元素的數(shù)據(jù)交互上明顯比JSONP強(qiáng)。
演示:[ 點(diǎn)此閱覽 ]
這個方案,YAHOO的同事已經(jīng)給出了詳細(xì)的demo,我就不重復(fù)了,演示demo出自YAHOO克軍之手。詳細(xì)的說明可以參看“懌飛的BLOG”,個人覺得方案四比window.name適用面更廣一些。
方案七、window.navigator
原理:window.navigator這個對象是在所有的Iframe中均可以訪問的對象。應(yīng)該是IE的一個漏洞!
缺點(diǎn):不支持IE外的瀏覽器下的跨域。嚴(yán)格的dtd申明后,該方法失效!
演示:[ 點(diǎn)此閱覽 ]
主頁面在主域:www.ioldfish.cn下,首先先申明一個Json用來保存所有頁面的高度window.navigator.PagesHeight={”":0};,然后根據(jù)name的屬性找到頁面的數(shù)據(jù)window.navigator.getData,最后將頁面的數(shù)據(jù)注冊到window.navigator.PagesHeight中。這里還定義了一個函數(shù)resetIframeData,在頁面加載的時(shí)候調(diào)用它,完成跨域的數(shù)據(jù)交互。注釋中詳細(xì)說明了參數(shù)的作用。
- <script type="text/javascript">
- window.navigator.PagesHeight={"":0};
- window.navigator.getData=function(pageName) {
- return window.navigator.PagesHeight[pageName];
- };
- window.navigator.putData=function(pageName,pageHeight)
- {
- window.navigator.PagesHeight[pageName]=pageHeight;
- };
- /*
- *iframeId:頁面中iframe的標(biāo)識id
- *key:子頁面自定義的json標(biāo)識,這里就是子頁面定義的"PortalData".
- *defaultData:無法取到值時(shí)候調(diào)用
- */
- function resetIframeData(iframeId,key,defualtData)
- {
- var obj=document.getElementById(iframeId);
- if(window.navigator.getData)
- {
- var pageHeight = window.navigator.getData(key);
- if(pageHeight && String(pageHeight).match(/\d+/))
- {
- obj.style.height=pageHeight+'px';
- }
- else
- {
- obj.style.height=defualtData + 'px';
- }
- }
- else
- {
- obj.style.height=defualtData + 'px';
- }
- }
- </script>
子頁面在主域:www.lzdaily.com下,獲取到頁面高度后,將高度存到主頁定義的Json中,Json標(biāo)識為”PortalData”,這里可以自定義。
- <script type="text/javascript">
- window.onload = function getPageData()
- {
- var pageHeight = document.body.scrollHeight;
- if(window.navigator.putData)
- {
- window.navigator.putData("PortalData",pageHeight);
- }
- }
- </script>
其實(shí)通過css也可以實(shí)現(xiàn)跨域,數(shù)據(jù)獲取的實(shí)質(zhì)其實(shí)就是動態(tài)載入一段CSS,然后解析其中的字段提取數(shù)據(jù),這個方法比較“猥瑣”,再這里就不多介紹了,當(dāng)然flash也可以實(shí)現(xiàn)跨域,只是還沒去實(shí)踐,實(shí)踐完了再補(bǔ)充。啥時(shí)候能補(bǔ)完呢?恩……
以上這么多方案,有可以“完美跨域”的嗎?單一的看,我想沒有吧,都有缺陷,但是只要不同情況下使用合適的方法,我想這才是最完美的!原來繞了一圈,我只是再說廢話,哎!不論怎么樣,還是希望這些廢話對還在苦苦追求“完美”的同學(xué)們有所啟發(fā)!