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 楼主给楼主更多写做的动力~
相关文章
相关标签/搜索