详解 

  前文提到用jsonp的方式来跨域获取数据,本文为大家介绍下如何利用window.name+iframe跨域获取数据。

  首先我们要简单了解下window.name和iframe的相关知识。iframe是html的一个标签,可以在网页中创建内联框架,有个src属性(指向文件地址,html、php等)可以选择内联框架的内容,可以看个例子(猛戳这里),大概了解下就行了。window.name(一般在js代码里出现)的值不是一个普通的全局变量,而是当前窗口的名字,这里要注意的是每个iframe都有包裹它的window,而这个window是top window的子窗口,而它自然也有window.name的属性,window.name属性的神奇之处在于name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。

  跨域解决方案似乎可以呼之欲出了,假设index.html页面请求远端服务器的数据,我们在该页面下新建一个iframe标签,该iframe的src属性指向服务器文件地址(利用iframe标签的跨域能力),服务器文件里设置好window.name的值(也就是该iframe的contentWindow的name值),然后在index.html里读取该iframe的window.name值,一切似乎水到渠成,代码如下:

<body>
  <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>'
?>

  但是不幸的是,报错了..

  提示啥协议、主机、端口三者要一致,这不是赤裸裸地告诉你跨域了么!为什么会这样,因为规定如果index.html页面和和该页面里的iframe框架的src如果不同源,则也无法操作框架里的任何东西,所以就取不到iframe框架的name值了,告诉你我们不是一家的,你也休想得到我这里的数据。既然要同源,那就换个src去指,前面说了无论怎样加载window.name值都不会变化,于是我们在index.html相同目录下,新建了个proxy.html的空页面,修改代码如下:

<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>

总结

  能使用这种方式跨域,有几个条件必不可少。

  1. iframe标签的跨域能力
  2. window.name属性值在文档刷新后依旧存在的能力

  再简单了解下window和contentWindow的一些知识。浏览器就会为原始文档创建一个 window 对象,再为每个框架(iframe)创建额外的 window 对象。这些额外的对象是原始窗口的子窗口,可能被原始窗口中发生的事件所影响。例如,关闭原始窗口将导致关闭全部子窗口。contentWindow属性是指指定的frame或者iframe所在的window对象。

使用方法

  很多人或许只关注使用方法,而对原理不怎么感冒。此法相对于jsonp复杂,使用方法也更复杂些。

  服务端一般输出一段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 解决跨域问题

利用window.name+iframe跨域获取数据详解的更多相关文章

  1. iframe 跨域问题解决方案 利用window.name+iframe跨域获取数据详解

    详解 前文提到用jsonp的方式来跨域获取数据,本文为大家介绍下如何利用window.name+iframe跨域获取数据. 首先我们要简单了解下window.name和iframe的相关知识.ifra ...

  2. 利用location.hash+iframe跨域获取数据详解

    前言 如果看懂了前文利用window.name+iframe跨域获取数据,那么此文也就很好理解了.一样都是动态插入一个iframe,然后把iframe的src指向服务端地址,而服务端同样都是输出一段j ...

  3. 利用window.name+iframe跨域获取接口数据

    最近做了一个表单广告,需要从接口读取数据,做完发现谷歌火狐下正常,360兼容和IE浏览器无法获取数据,以下是鲜明的对比:      调试发现报错了: 然后开发把接口改成支持windowname,一开始 ...

  4. js中几种实用的跨域方法原理详解(转)

    今天研究js跨域问题的时候发现一篇好博,非常详细地讲解了js几种跨域方法的原理,特分享一下. 原博地址:http://www.cnblogs.com/2050/p/3191744.html 下面正文开 ...

  5. js中几种实用的跨域方法原理详解

    这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被 ...

  6. [转]js中几种实用的跨域方法原理详解

    转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...

  7. js中几种实用的跨域方法原理详解【转】

    源地址:http://www.cnblogs.com/2050/p/3191744.html 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通 ...

  8. 【转】js中几种实用的跨域方法原理详解

    这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被 ...

  9. jQuery之ajax的跨域获取数据

    如果获取的数据文件存放在远程服务器上(域名不同,也就是跨域获取数据),则需要使用jsonp类型.使用这种类型的话,会创建一个查询字符串参数 callback=? ,这个参数会加在请求的URL后面.服务 ...

随机推荐

  1. olcal数据库经典SQL语句大全

    基于olacle自带的表 第一篇 -----1.列出至少有一个员工的所有部门. oracle 一些经典sql第一篇 --------1.列出至少有一个员工的所有部门.---------  SQL> ...

  2. Nginx为什么比Apache Httpd高效:原理篇

    一.进程.线程? 进程是具有一定独立功能的,在计算机中已经运行的程序的实体.在早期系统中(如linux 2.4以前),进程是基本运作单位,在支持线程的系统中(如windows,linux2.6)中,线 ...

  3. cocos2d-x之value

    bool HelloWorld::init() { if ( !Layer::init() ) { return false; } Size visibleSize = Director::getIn ...

  4. SQL2008 提示评估期已过的解决方法

    提示窗口: 解决步骤: 第一步:进入SQL2008配置工具中的安装中心 第二步:再进入维护界面,选择版本升级 第三步:进入产品密钥,输入密钥 第四步:一直点下一步,直到升级完毕. SQL Server ...

  5. textarea文本简单样式编辑

    第一种方法采用替换:就是将文本域的换号符号\r\n,替换成其他符号,存入数据库,然后显示的时候再转换回来: //转换换行符$str=preg_replace("/\r\n|\r|\n/&qu ...

  6. [转]Oracle分页之二:自定义web分页控件的封装

    本文转自:http://www.cnblogs.com/scy251147/archive/2011/04/16/2018326.html 上节中,讲述的就是Oracle存储过程分页的使用方式,但是如 ...

  7. C之五子棋

    #include <stdio.h> #include <stdlib.h> #define N 15 ][N + ] = { }; ; void initGame(void) ...

  8. 【C#】SQL数据库助手类2.0(自用)

    using System; using System.Collections.Generic; using System.Configuration; using System.Data; using ...

  9. Verilog (一) assignment, register and net

    Verilog 区分大小写, 且所有关键字都是小写 1  register = storage keyword reg; default x; variable that can hold value ...

  10. RMQ之ST算法模板

    #include<stdio.h> #include<string.h> #include<iostream> using namespace std; ; ],M ...