iframe 跨域問題解決方案 利用window.name+iframe跨域獲取數據詳解

詳解 

  前文提到用jsonp的方式來跨域獲取數據,本文爲你們介紹下如何利用window.name+iframe跨域獲取數據。javascript

  首先咱們要簡單瞭解下window.name和iframe的相關知識。iframe是html的一個標籤,能夠在網頁中建立內聯框架,有個src屬性(指向文件地址,html、php等)能夠選擇內聯框架的內容,能夠看個例子(猛戳這裏),大概瞭解下就好了。window.name(通常在js代碼裏出現)的值不是一個普通的全局變量,而是當前窗口的名字,這裏要注意的是每一個iframe都有包裹它的window,而這個window是top window的子窗口,而它天然也有window.name的屬性,window.name屬性的神奇之處在於name 值在不一樣的頁面(甚至不一樣域名)加載後依舊存在(若是沒修改則值不會變化),而且能夠支持很是長的 name 值(2MB)。php

  跨域解決方案彷佛能夠呼之欲出了,假設index.html頁面請求遠端服務器的數據,咱們在該頁面下新建一個iframe標籤,該iframe的src屬性指向服務器文件地址(利用iframe標籤的跨域能力),服務器文件裏設置好window.name的值(也就是該iframe的contentWindow的name值),而後在index.html裏讀取該iframe的window.name值,一切彷佛水到渠成,代碼以下:html

  <script type="text/javascript"> 
    iframe = document.createElement('iframe'),
    iframe.src = 'http://localhost:8080/data.php';
    document.body.appendChild(iframe);
    iframe.onload = function() {
      console.log(iframe.contentWindow.name)
    };
  </script>
</body>
<?php
  echo '<script> window.name = "{\"name\":\"hanzichi\", \"age\":10}"; </script>'
?>

  可是不幸的是,報錯了..java

  提示啥協議、主機、端口三者要一致,這不是赤裸裸地告訴你跨域了麼!爲何會這樣,由於規定若是index.html頁面和和該頁面裏的iframe框架的src若是不一樣源,則也沒法操做框架裏的任何東西,因此就取不到iframe框架的name值了,告訴你咱們不是一家的,你也休想獲得我這裏的數據。既然要同源,那就換個src去指,前面說了不管怎樣加載window.name值都不會變化,因而咱們在index.html相同目錄下,新建了個proxy.html的空頁面,修改代碼以下:git

<body>
  <script type="text/javascript"> 
    iframe = document.createElement('iframe'),
    iframe.src = 'http://localhost:8080/data.php';
    document.body.appendChild(iframe);
    iframe.onload = function() {
      iframe.src = 'http://localhost:81/cross-domain/proxy.html';
      console.log(iframe.contentWindow.name)
    };
  </script>
</body>
 

  理想彷佛很美好,在iframe載入過程當中,迅速重置iframe.src的指向,使之與index.html同源,那麼index頁面就能去獲取它的name值了!可是現實是殘酷的,iframe在現實中的表現是一直不停地刷新,也很好理解,每次觸發onload時間後,重置src,至關於從新載入頁面,又觸發onload事件,因而就不停地刷新了(可是須要的數據仍是能輸出的)。修改後代碼以下:程序員

 
<body>
  <script type="text/javascript"> 
    iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    var state = 0;
    
    iframe.onload = function() {
      if(state === 1) {
          var data = JSON.parse(iframe.contentWindow.name);
          console.log(data);
          iframe.contentWindow.document.write('');
          iframe.contentWindow.close();
        document.body.removeChild(iframe);
      } else if(state === 0) {
          state = 1;
          iframe.contentWindow.location = 'http://localhost:81/cross-domain/proxy.html';
      }
    };

    iframe.src = 'http://localhost:8080/data.php';
    document.body.appendChild(iframe);
  </script>
</body>
 

總結

  能使用這種方式跨域,有幾個條件必不可少。github

  1. iframe標籤的跨域能力
  2. window.name屬性值在文檔刷新後依舊存在的能力

  再簡單瞭解下window和contentWindow的一些知識。瀏覽器就會爲原始文檔建立一個 window 對象,再爲每一個框架(iframe)建立額外的 window 對象。這些額外的對象是原始窗口的子窗口,可能被原始窗口中發生的事件所影響。例如,關閉原始窗口將致使關閉所有子窗口。contentWindow屬性是指指定的frame或者iframe所在的window對象。算法

使用方法

  不少人或許只關注使用方法,而對原理不怎麼感冒。此法相對於jsonp複雜,使用方法也更復雜些。json

  服務端通常輸出一段js代碼,例以下面這樣:跨域

<?php
  echo '<script> window.name = "{\"name\":\"hanzichi\", \"age\":10}"; </script>'
?>

  window.name的值是字符串形式,也是須要傳遞給客戶端的數據(固然若是有須要服務端也能夠先處理傳過來的數據,這裏僅爲只有數據回傳),有須要本身再去解析(如字符串->json數據)。

  index.html頁面,我把方法封裝成了函數:

 
<body>
  <script type="text/javascript"> 
    function crossDomain(url, fn) {
      iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      var state = 0;
    
      iframe.onload = function() {
        if(state === 1) {
          fn(iframe.contentWindow.name);
          iframe.contentWindow.document.write('');
          iframe.contentWindow.close();
          document.body.removeChild(iframe);
        } else if(state === 0) {
          state = 1;
          iframe.contentWindow.location = 'http://localhost:81/cross-domain/proxy.html';
        }
      };

      iframe.src = url;
      document.body.appendChild(iframe);
    } 
    
    // 調用
    // 服務器地址
    var url = 'http://localhost:8080/data.php';
    crossDomain(url, function(data) { // 處理數據 data就是window.name的值(string)
      var data = JSON.parse(iframe.contentWindow.name);
      console.log(data);
    });
  </script>
</body>
 

  這裏還有一點小問題,ie的兼容(iframe的onload ie下彷佛要用attachEvent,以及json對於ie的兼容),有興趣的朋友本身去研究了(能夠參考下參考文章);另外proxy代理頁面能夠沒有這個文件,會報404可是不影響功能(可是路徑必定要和index頁面同源)。

  還有一點值得思考的是iframe的src重定向的時候,代碼:

iframe.contentWindow.location = 'http://localhost:81/cross-domain/proxy.html';

  這時iframe.src指向的仍是服務端頁面,這裏的代碼若是用iframe.src = 'http://localhost:81/cross-domain/proxy.html';代替也是能夠輸出結果的,聰明的你知道區別嗎?

參考

  1. js中幾種實用的跨域方法原理詳解
  2. JS 使用window.name跨域實踐
  3. 使用 window.name 解決跨域問題
多是史上最詳細的 underscore 源碼剖析: https://github.com/hanzichi/underscore-analysis
程序員都應該學點算法: https://github.com/hanzichi/leetcode
瞭解博主韓子遲: http://www.cnblogs.com/zichi/p/about.html
GitHub: https://github.com/hanzichi  Follow 樓主給樓主更多寫做的動力~
相關文章
相關標籤/搜索