這里接上篇:js中各種跨域問題實戰小結(一)
后面繼續學習的過程中,對上面第一篇有稍作休整。下面繼續第二部分:
-->5.利用iframe和location.hash
-->6.window.name跨域實現
利用iframe和location.hash實現跨域
想必有很多人像我之前一樣,或許只知道上面文中所說的那幾種方法。所以,我剛了解到可以用iframe和location.hash來實現跨域的時候,我會想,為什么他們可以實現。iframe是什么,有什么特性,location.hash是什么又有什么特性。
准備:
>>1.MDN上說的<iframe>:HTML 的<iframe>(HTML inline框架元素)是一個嵌套的瀏覽上下文,他可以有效地嵌入另一個HTML頁面到當前頁面。在HTML 4.01中,一個文件可能包含一個頭和一個身體或頭部和一個框架集,而不是一個身體和一個框架集。然而,一個< iframe >可以被應用在正常的文件體。每個瀏覽上下文有它自己的會話歷史和活動文檔。瀏覽上下文包含嵌入的內容叫做父瀏覽上下文。頂級的瀏覽上下文(沒有父)是典型的瀏覽器窗口。
了解到了<iframe>,我覺得另外兩個相似的標簽<frame>和<frameset>可以一起拿過來看下啦,這有篇文章放在一起總結了下:Frameset,Frame和Iframe
>>2.window.location:打開chrome的console,一起來試一下,很快就懂啦。我們從最基本的開始:
2.1 可以跳轉到另一個頁面:打開百度搜索的主頁,然后在console中輸入下面代碼:
window.location = "http://www.cnblogs.com/skylar/";
看,是不是跳轉到我的博客主頁啦。
2.2 強制從服務器重新加載頁面:既然來到了我的博客主頁,就先別走,繼續輸入下面代碼:
window.location.reload(true);
可以看到頁面被重新加載了。
2.3 然后可以點擊進去比如js中各種跨域問題實戰小結(一)這篇文章,console輸入:
location.hash = '#comments';
看看頁面是不是跳到了最下面評論的位置。
其實還有兩個特性:比如說alert當前url屬性,通過修改window.location.search屬性向服務器發送一個字符串,這你可以移步這里自行腦補:MDN window.location 。
這里有用的是下面這個例子:location.hash理解demo
這里可以查看代碼:https://github.com/zhangmengxue,因為我們就是要利用location.hash的特性來加上iframe來實現跨域,看完了這個demo,我們就可以開始實現跨域了。(請用chrome打開,還有些需要總結的兼容性問題)
實現:
我為了模擬兩個不同的域,在SAE上面創建了兩個wordpress應用,如果你之前也不懂SAE是什么的話,這個小簡介:新浪SAE 會讓你大致了解到SAE是做什么的,如何開始使用它。我的兩個應用:
看到url了吧,是不同域喔,環境模擬好了,我可以開始嘗試跨域了。
在[http://1.daeskylar.sinaapp.com/]下有兩個文件 a.html和c.html;
在[http://1.skylarisdae.sinaapp.com/]下有文件 b.html
我想要在a.html中訪問我在b.html中模擬的數據Helloworld!
好呀,那現在點擊 有a.html和c.html的這個域 訪問我在這個域上的a.html文件,它應該會告訴你他是a.html頁面,然后他會訪問到了我放在b.html中的HelloWorld!
跨域這樣就實現了喔,那具體是怎樣實現的呢?
原理:
a.html想和b.html通信(在a.html中動態創建一個b.html的iframe來發送請求);
但是由於“同源策略”的限制他們無法進行交流(b.html無法返回數據),於是就找個中間人:與a.html同域的c.html;
b.html將數據傳給c.html(b.html中創建c.html的iframe),由於c.html和a.html同源,於是可通過c.html將返回的數據傳回給a.html,從而達到跨域的效果。
三個頁面之間傳遞參數用的是location.hash,改變hash並不會導致頁面刷新(這點很重要)。
------------------------------------------------------------------------------------------------------------------------------------------------
a.html---①--->b.html----②--->c.html---③--->a.html——>a.html便獲得到了b.html中的數據
①通過iframe的location.hash傳參數給b.html ;
②通過iframe的location.hash傳參數給c.html ;
③同域傳給a.html。
-------------------------------------------------------------------------------------------------------------------------------------------------
代碼:
a.html:

1 <!doctype html> 2 <html> 3 <head> 4 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> 5 <title>localhost:a.html</title> 6 7 <style type="text/css"> 8 9 </style> 10 </head> 11 12 <body> 13 <script type="text/javascript"> 14 alert('我是a頁面'); 15 function sendRequest(){ 16 //動態創建個iframe 17 var ifr = document.createElement('iframe'); 18 ifr.style.display = 'none'; 19 //跨域發送請求給b.html,參數是#sayHello 20 ifr.src = 'http://1.skylarisdae.sinaapp.com/b.html#sayHello'; 21 document.body.appendChild(ifr); 22 } 23 function checkHash(){ 24 var data = location.hash?location.hash.substring(1):''; 25 if(data){ 26 //這里處理返回值 27 alert(data); 28 location.hash = ''; 29 } 30 } 31 setInterval(checkHash,1000); 32 window.onload = sendRequest; 33 </script> 34 </body> 35 </html>
c.html:

1 <!doctype html> 2 <html> 3 <head> 4 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> 5 <title>localhost:c.html</title> 6 7 <style type="text/css"> 8 9 </style> 10 </head> 11 12 <body> 13 <script type="text/javascript"> 14 //因為c.html和a.html屬於同一個域,所以可以通過改變其location.hash的值,可以通過parent.parent獲取a.html的window對象 15 parent.parent.location.hash = self.location.hash.substring(1); 16 </script> 17 </body> 18 </html>
b.html:

1 <!doctype html> 2 <html> 3 <head> 4 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> 5 <title>另某個域的:b.html</title> 6 7 <style type="text/css"> 8 9 </style> 10 </head> 11 12 <body> 13 <script type="text/javascript"> 14 function checkHash(){ 15 var data = ''; 16 //模擬一個簡單的參數處理操作 17 switch(location.hash){ 18 case '#sayHello': 19 data = 'HelloWorld'; 20 break; 21 case '#sayHi': 22 data = 'HiWorld'; 23 break; 24 default : break; 25 } 26 data && callBack('#'+data); 27 } 28 function callBack(hash){ 29 var proxy = document.createElement('iframe'); 30 proxy.style.display = 'none'; 31 proxy.src = 'http://1.daeskylar.sinaapp.com/c.html'+hash; 32 document.body.appendChild(proxy); 33 } 34 window.onload = checkHash; 35 </script> 36 </body> 37 </html>
這里參考了:javascript跨域詳解
利用window.name實現跨域
實現:
我還是利用上面我在sae上面創建的應用:
在[http://1.daeskylar.sinaapp.com/]下有兩個文件 wantdata.html和proxy.html;
在[http://1.skylarisdae.sinaapp.com/]下有文件 data.html。
我想要在wantdata.html中訪問到我在data.html中存在window.name中的數據,是個字符串,就叫‘我拿到數據啦!’。
訪問這里:http://1.daeskylar.sinaapp.com/wantdata.html 應該可以看到alert出的數據喔。
原理:
MDN上的window.name是這樣說的:
窗口的名稱,主要用於設置超鏈接和表格對象。Windows不需要有名字。
它也被用於提供跨域通信的一些框架(例如,sessionvars和Dojo的DojoX。IO。windowname)為JSONP更安全的替代方案。
window.name 的美妙之處在於:name 值在不同的頁面(甚至不同域名)加載后依舊存在,並且可以支持非常長的 name 值(2MB)。
這篇文章講解的非常詳細,很值得閱讀:使用window.name解決跨域問題
實現起來基本步驟如下:
1.創建一個iframe,把其src指向目標頁面(提供web service的頁面,該目標頁面會把數據附加到這個iframe的window.name上,大小一般為2M,IE和firefox下可以大至32M左右;數據格式可以自定義,如json字符串);
2.監聽iframe的onload事件,在此事件中立即設置這個iframe的src指向本地域的某個頁面,由本地域的這個頁面讀取iframe的window.name。
3.獲取數據以后銷毀這個iframe,釋放內存;這也保證了安全(不被其他域frame js訪問)。
總結起來即:iframe的src屬性由外域轉向本地域,跨域數據即由iframe的window.name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操作。
代碼:
wantdata.html

1 <!doctype html> 2 <html> 3 <head> 4 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> 5 <title>window.name跨域demo</title> 6 7 <style type="text/css"> 8 9 </style> 10 </head> 11 12 <body> 13 <script type="text/javascript"> 14 var state = 0, 15 iframe = document.createElement('iframe'), 16 loadfn = function() { 17 if (state === 1) { 18 var data = iframe.contentWindow.name; // 讀取數據 19 alert(data); //彈出'拿到數據啦!' 20 //下面立即銷毀iframe,釋放內存,也保證安全 21 iframe.contentWindow.document.write(''); 22 iframe.contentWindow.close(); 23 document.body.removeChild(iframe); 24 } else if (state === 0) { 25 state = 1; 26 iframe.contentWindow.location = "http://1.daeskylar.sinaapp.com/proxy.html"; // 設置的代理文件 27 } 28 }; 29 iframe.src = 'http://1.skylarisdae.sinaapp.com/data.html'; 30 if (iframe.attachEvent) { 31 iframe.attachEvent('onload', loadfn); 32 } else { 33 iframe.onload = loadfn; 34 } 35 document.body.appendChild(iframe); 36 </script> 37 </body> 38 </html>
data.html

1 <!doctype html> 2 <html> 3 <head> 4 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> 5 <title>data頁</title> 6 7 <style type="text/css"> 8 9 </style> 10 </head> 11 12 <body> 13 <script type="text/javascript"> 14 window.name = '拿到跨域的數據啦!'; // 這里是要傳輸的數據,大小一般為2M,IE和firefox下可以大至32M左右 15 // 數據格式可以自定義,如json、字符串 16 </script> 17 </body> 18 </html>
proxy.html

1 <!doctype html> 2 <html> 3 <head> 4 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> 5 <title>window.name跨域demo</title> 6 7 <style type="text/css"> 8 9 </style> 10 </head> 11 12 <body> 13 <span>這里是代理空文件</span> 14 <script type="text/javascript"> 15 16 </script> 17 </body> 18 </html>
這里參考了:window.name實現跨域數據傳輸 使用window.name解決跨域問題
從一大早寫到現在了,午覺也沒睡,寫不動了,后面再來個三吧,補上:
-->7.HTML5 postMessage實現跨域