window.name 传输技术,原本是 Thomas Frank 用于解决 cookie 的一些劣势(每个域名 4 x 20 Kb 的限制、数据只能是字符串、设置和获取 cookie 语法的复杂等等)而发明的(详细见原文:《Session variables without cookies》),后来 Kris Zyp 在此方法的基础上强化了 window.name 传输 ,并引入到了 Dojo dojox.io.windowName),用来解决跨域数据传输问题。

window.name 的美妙之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

window.name 传输技术的基本原理和步骤为:
window.name 技术的基本原理和步骤图示

name 在浏览器环境中是一个全局/window对象的属性,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。通过在 iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web 服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 Web 服务页面被加载后,必须导航 frame 回到原始域。同源策略依旧防止其他 frame 访问 name 属性。一旦 name 属性获得,销毁 frame 。

在最顶层,name 属性是不安全的,对于所有后续页面,设置在 name 属性中的任何信息都是可获得的。然而 windowName 模块总是在一个 iframe 中加载资源,并且一旦获取到数据,或者当你在最顶层浏览了一个新页面,这个 iframe 将被销毁,所以其他页面永远访问不到 window.name 属性。

基本实现代码,基于 YUI,源自克军写的样例

(function(){
    var YUD = YAHOO.util.Dom, YUE = YAHOO.util.Event;
    
    dataRequest = {
        _doc: document,
        cfg: {
            proxyUrl: 'proxy.html'
        }
    };

    dataRequest.send = function(sUrl, fnCallBack){
        if(!sUrl || typeof sUrl !== 'string'){
            return;
        }
        
        sUrl += (sUrl.indexOf('?') > 0 ? '&' : '?') + 'windowname=true';

        var frame = this._doc.createElement('iframe'), state = 0, self = this;
        this._doc.body.appendChild(frame);
        frame.style.display = 'none';

        var clear = function(){
            try{
                frame.contentWindow.document.write('');
                frame.contentWindow.close();
                self._doc.body.removeChild(frame);
            }catch(e){}
        };

        var getData = function(){
            try{
                var da = frame.contentWindow.name;
            }catch(e){}
            clear();
            if(fnCallBack && typeof fnCallBack === 'function'){
                fnCallBack(da);
            }
        };

        YUE.on(frame, 'load', function(){
            if(state === 1){
                getData();
            } else if(state === 0){
                state = 1;
                frame.contentWindow.location = self.cfg.proxyUrl;
            }
        });

        frame.src = sUrl;
    };    
})();

Web 服务器如何提供 window.name 数据

为了让 Web 服务器实现 window.name,服务器应该只寻找请求中是否包含 windowname 参数。如果包含了 windowname 参数,服务器应该返回一个设置了 window.name 字符串值的 HTML 文档,回应此请求并传送到客户端。例如:
http://www.planabc.net/getdata.html?windowname=true

如果服务器想用 Hello 响应客服端,它应该返回一个 HTML 页面:

<html>
    <script type="text/javascript">
        window.name="Hello"; 
    </script>
</html>

同样也可以转换为 JSON 数据:

<html>
    <script type="text/javascript">
        window.name='{"foo":"bar"}'; 
    </script>
</html>

如果你手动创建资源,书写大量的多行的 JSON 对象为一个引用的字符串应该是比较困难的并且易于出错的。可以使用这样的 HTML 样例简单的创建 JSON 数据,将会转换为一个 JSON 字符串而无需手动转义 JSON 为字符串:

<html>
    <script type="\'text/javascript\'">
        window.name = document.getElementsByTagName("script")[0].innerHTML.match(/temp\s*=([\w\W]*)/)[1];
        temp= {
            foo:"bar", // put json data here
            baz:"foo"
        }  
    </script> 
</html>

同样的,如果你想传递 HTML/XML 数据,这里有一个样例实现,而无需手动将这些数据转换成字符串:

<html>
    <body>
        <p id="content">  
            some <strong>html/xml-style</strong>data  
        </p>  
    </body>  
    <script type="text/javascript"> 
        window.name = document.getElementById("content").innerHTML;  
    </script>
</html>

window.name 传输技术相比其他的跨域传输的一些优势:

  1. 它是安全的。也就是说,它和其他的基于安全传输的 frame 一样安全,例如 Fragment Identifier messaging (FIM)和 Subspace。(I)Frames 也有他们自己的安全问题,由于 frame 可以改变其他 frame 的 location,但是这个是非常不同的安全溢出,通常不太严重。
  2. 它比 FIM 更快,因为它不用处理小数据包大小的 Fragment Identifier ,并且它不会有更多的 IE 上的“机关枪”声音效果。它也比 Subspace 快,Subspace 需要加载两个 Iframe 和两个本地的 HTML 文件来处理一个请求。window.name 仅需要一个 Iframe 和一个本地文件。
  3. 它比 FIM 和 Subspace 更简单和安全。FIM 稍微复杂,而 Subspace 非常复杂。Subspace 也有一些额外的限制和安装要求,如预先声明所有的目标主机和拥有针对若干不同特殊主机的 DNS 入口。window.name 非常简单和容易使用。
  4. 它不需要任何插件(比如 Flash)或者替代技术(例如 Java)。


共有50 条评论

  1. 1. 头像 Macji

    对于好文,我只会说沙发。

  2. 2. 头像 ZhuZhe

    感觉js发展越来越畸形了。

  3. 3. 头像 emptyhua

    好文~~

  4. 4. 头像 nobita

    如果包含 w了indowname 参数
    笔误~

  5. 5. 头像 nobita

    既然用 YUI,那我就 lazy-load 了,更适合 lazy-bone。
    收藏了~

  6. 6. 头像 supnate

    赞下

  7. 7. 头像 黑龙江SEO

    介绍很详细,支持一下!

  8. 8. 头像 哉崽

    思路不错,赞一个,
    不过提供的示例代码MS有点疑问
    1.类似“state === 1”的条件判断,没必要用恒等式吧,效率更高吗?不解。
    2.没搞明白要多个proxyUrl做啥,还得弄个 state 来判断,再重新跳转。数据直接获取到,callback,clear,了事,为啥非得跳到proxy.html? 为了证明页面切换数据还在?(个人认为一般应用不需要state )
    3.我还没模拟过正式的跨域请求,本地测试了下克军的代码,去除YUI,直接用frame.load,好像只有 Chrome能正确取得数据,IE根本没响应(去除state后解决),FF娶不到数据(把frame.src 改成 frame.contentWindow.location,或者setTimeout延迟请求解决),Chrome正常

    另外,克军的演示示例,其他浏览器执行正常,Chrome直接崩溃
    难道YUI对load做了特殊处理?

    结论:

    本地静态测试,
    未跨域,没有服务,不用YUI,
    兼容IE,得去除state
    兼容FF,得把frame.src 改成 frame.contentWindow.location
    修改后测试通过(WIN XP SP2——IE6,IE7,FF3.0.1,OPERA9.5.2,Chrome0.2.149)

    等下进行正式跨域测试,呵呵

    在下不才,望blank指点一二

  9. 9. 头像 怿飞

    @哉崽 下面的序号针对你问题的序号
    1、=== 是为了严格的匹配
    2、如果没有proxyUrl,无法跨域访问(同源策略),也就是基本无法获取window.name,在原理中已经提到。
    3、src未错误,只有再次转向时,需要用frame.contentWindow.location。

  10. 10. 头像 哉崽

    郁闷,跨不同域名,只有opera浏览器能获取到数据
    其他浏览器都是 undefined

    blank有用这个方法实现过跨异域的吗?不是二级域名,是不同域名的那种,如果有测试成功的示例代码,请发一份到我邮箱(joeke.cn@gmail.com),学些下,谢谢!

  11. 11. 头像 怿飞

    @哉崽 上面的 window.name 就是用来解决跨不同域名的方法呀–!汗的,包括后面在谈优点时提及的两种方式也是用来解决跨域问题的。至于opera,那是因为支持了HTML5中跨域传输

  12. 12. 头像 哉崽

    我就是复制了你的基本实现代码,只是去除了YUI部分,然后在跨域和非跨域两种环境下测试,搞出这些问题的。所以也很汗,代码发到你邮箱了,另外在外网服务器上放了不同域的测试数据,有时间帮忙看看下,谢谢!

  13. 13. 头像 哉崽

    问题解决啦,
    我犯了个低级错误,把proxyUrl指到数据源的域下了,晕啊

    另外,如果不在web服务环境下,直接本地建个目录测试,确实会发生一些有趣的问题,就像我第一个回帖描述的

  14. 14. 头像 qq空间

    没有服务,不用YUI

  15. 15. 头像 战栗

    没有看出这种方式与动态脚本相比有什么好处。
    这种方式也是只能解决get方式的请求,post请求并没有解决啊!

  16. 16. 头像 战栗

    firefox下面可以,ie下面不行
    dataRequest = {
    _doc: document,
    cfg: {
    proxyUrl: ‘http://localhost:8080/jsp-examples/proxy.html’
    }
    };

    dataRequest.send = function(sUrl, fnCallBack){
    //alert(“send”);
    if(!sUrl || typeof sUrl !== ‘string’){
    return;
    }

    sUrl += (sUrl.indexOf(‘?’) > 0 ? ‘&’ : ‘?’) + ‘windowname=true’;

    var frame = this._doc.createElement(‘iframe’), state = 0, self = this;
    this._doc.body.appendChild(frame);
    frame.style.display = ‘none’;

    var clear = function(){
    try{
    frame.contentWindow.document.write(”);
    frame.contentWindow.close();
    self._doc.body.removeChild(frame);
    }catch(e){}
    };

    var getData = function(){
    try{
    var da = frame.contentWindow.name;
    }catch(e){}
    clear();
    if(fnCallBack && typeof fnCallBack === ‘function’){
    fnCallBack(da);
    }
    };

    function frameLoad(){
    //alert(“onLoad”);
    if(state === 1){
    alert(“state == 1”);
    getData();
    } else if(state === 0){
    state = 1;
    //alert(“state == 0”);
    frame.contentWindow.location = “http://localhost:8080/jsp-examples/proxy.html”;
    }
    };
    frame.onload = frameLoad;

    frame.src = sUrl;
    };

    dataRequest.send(‘http://localhost:9080/domain/test.html’,function(data){
    alert(data);
    });

  17. 17. 头像 怿飞

    @战栗 所有的A级浏览器均测试过,都可以的

  18. 18. 头像 怿飞

    @战栗 对于跨域数据之间完全互通,我还是推荐你使用 Julien Lecomte 提出的跨域方式。

    跨域

  19. 19. 头像 he

    @战栗 frame.onload 在ie下好像有问题, 但是有替代的解决办法:
    http://blog.guitarbean.com/2008/08/iframe-onload-event-monitoring.html

  20. 20. 头像 welefen

    window.name还可以用来缓存JS、CSS等静态文件

  21. 21. 陈成的博客 — iframe全跨域高度自适应解决方案

    […] 使用window.name解决跨域问题 […]

  22. 22. 头像 我爱看电影网

    貌似可以实现对多站用户的整合。。。

  23. 23. 头像 tencent coolery

    这里利用了url来实现数据的转移 蛮有意思

  24. 24. 头像 lenel

    果然够巧,只是有个问题在FireFox会增加history的记录.
    如果只有data域在控制下,提供给多个request页面服务对request域不够友好.
    之前通过互传Hash值的简单跨域通信方式也会遇到扰乱FireFox浏览器history的问题.
    不知道文中提到的FIM或者Subspace是否可以避免?
    如果data域在控制下,并对外提供服务,是否可以通过参数接收request域的proxy页面地址,在data域页面执行后自行导入proxy页.这是不是18楼那张图表达的意思?
    能不能也比较一下这种实现方式和on-demand script方案的各自特点和适合的应用场景?

  25. 25. 头像 jay

    safari下用js改变location.hash的时候页面会刷新,请教大虾有什么绝招不

  26. 26. 头像 脚本爱好者

    使用window.name还是需要不同域的 service去配合,既然这样,用script方式加载,然后直接返回json数据不是更好?

  27. 27. 阿里巴巴(软件)开发者博客 » Blog Archive » 实战window.name解决跨域问题

    […] 之前在怿飞的博客上看到一篇关于window.name解决跨域问题的文章,给web前端的跨域问题提供了另外一种选择,但是里面的例子写得不是很简洁,这里从一个简单的例子,来说明该机制的应用。具体的机制说明可以参考http://www.planabc.net/2008/09/01/window_name_transport/ […]

  28. 28. 头像 天堂左我往右

    怿飞改天写篇FIM 和 Subspace 的文章吧…

  29. 29. 头像 nttdocomo

    这跟用script标签来实现跨域数据传输有什么不一样啊?

  30. 30. 头像 kim wang

    这种方案在ie和ff下都会显示请求进度……

  31. 31. 头像 forbe

    这个的确是个方案。但是问题在于FF对IFRAME的缓存导致F5之后。是不会请求到数据的。除非CTRL+F5
    所以这个显得鸡肋了..
    上代码如下

    /* -> 此实现在FF下按F5有代码不可控制的 IFRAME缓存问题, 废除.*/
    function XHR(sUrl, fnCallBack) {
    fnCallBack = fnCallBack || new Function();
    sUrl += (sUrl.indexOf(‘?’) > 0 ? ‘&’: ‘?’) + ‘WINDOWNAME=true’;
    var frame = document.createElement(‘iframe’),
    state = 0;
    document.body.appendChild(frame).style.display = ‘none’;
    frame.readyState ? frame.attachEvent(“onload”, done) : frame.onload = done;
    frame.src = sUrl;
    function getData() {
    try {
    fnCallBack(frame.contentWindow.name);
    frame.contentWindow.document.write(”);
    frame.contentWindow.close();
    document.body.removeChild(frame);

    } catch(e) {
    fnCallBack(‘{code:999}’)

    }

    }
    function done() {
    state == 1 ? getData() : (state = 1, frame.contentWindow.location = “about:blank”)
    }

    }

  32. 32. 头像 forbe

    同时: 这不是一种完美的方式,
    1.要求服务端返回一段html代码。同时必须有js代码指定window.name=”返回的值”; 可悲的是这值必须是text.
    2.加上我所说的FF的IFRAME的缓存问题。
    3.同时,在IE下会有滴滴声。用户体验不好.
    所以这个方案是失败的. 还是用script注入方式好一点.

  33. 33. 头像 风行

    漂亮

  34. 34. 关于“跨域”问题的总结 « Play Google

    […] window.name:name 在浏览器环境中是一个全局/window对象的属性,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。通过在 iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web 服务发送的信息。参考使用 window.name 解决跨域问题 […]

  35. 35. Ajax跨域访问解决方案 - Hi, I’m Adam Lu.

    […] http://www.planabc.net/2008/09/01/window_name_transport/ […]

  36. 36. 近乎完美的简单 JS 跨域解决方式 –window.name | Wang Jun's Blog

    […] 无意中看到一篇文章《使用 window.name 解决跨域问题》……豁然开朗! 俺的问题终于解决了。 […]

  37. 37. The well. Name realize cross-realm data transmission

    […] articles:window.name Transport、Session variables without cookies、Use well. Name solve cross-realm problem、Use well. Name realize cross-realm access basic steps、KeJun writing samples。 Share this on: […]

  38. 38. 结合iframe,window.name实现跨域访问 | 工夫茶

    […] 然后又看了怿飞的博客里的两篇文章,很受启发 使用 window.name 解决跨域问题 判断 iframe 是否加载完成的完美方法 自己用纯js也模仿着实现了一个 […]

  39. 39. 新浪微博JS SDK跨域使用要点 » Sean's Blog

    […] 使用 window.name 解决跨域问题 […]

  40. 40. 大航路 » Blog Archive » AJAX跨域请求的实现 - 超越时代的妄想

    […] 不同主域的时候使用Iframe,其中又有location.hash(参考这里),window.name(参考这里)或是代理iframe(后面有说明)几种方式实现数据交互 […]

  41. 41. 头像 sking7

    chrome好像禁止了访问ifrmae的name。。

  42. 42. 头像 shanlu

    这中跨域是两个域都是在自己的支配范围内。现在有这样的个例子,就是a.com不在我的控制范围内,在a.com的页面中通过浏览器booklet的脚本,动态插入一个b.com下的iframe进去,如何将a.com下面的数据传给b.com下面的iframe,然后iframe用异步将数据提交到b.com的后台成功后,将这个状态再传给a.com下booklet脚本?盼解答,谢谢。

  43. 43. 也谈跨域数据交互解决方案 | 阿龙的博客

    […] 如 果站点B有一个proxy页面,用原生Ajax(XMLHttpRequest)对B站其他页面进行各种数据交互,那么我们在A站用iFrame引入这个 proxy页面,只需要解决iFrame跨域问题就可以了。实际上,如果A和B属于相同大域,设置两边的document.domain为根域名就OK 了;如果是完全不同的两个域,也有许多现成的解决方案,例如经典的window.name。更妙的是,除开IE6、IE7,几乎所有现代浏览器都支持用window.postMessage实现不同iFrame的数据通讯。pmxdr就是这样一个库,利用postMessage把数据传给隐藏的站外iFrame来实现跨域Ajax,libxdr对它进行了进一步的封装,使之更好用: […]

  44. 44. 跨域通信与实验 : zciii的博客2

    […] http://www.planabc.net/2008/09/01/window_name_transport/ […]

  45. 45. 跨域通信与实验 : zciii的博客2

    […]     http://www.planabc.net/2008/09/01/window_name_transport/ […]

  46. 46. 跨域通信与实验 : zciii的博客2

    […]     http://www.planabc.net/2008/09/01/window_name_transport/ […]

  47. 47. 页面加载时间度量 | oldj’s blog

    […] 那么,用户离开前一个页面的时间 t0 保存在哪里呢?可以看看 IBM 奥斯汀研究实验室在 2001 年写的这篇论文:Measuring Client-Perceived Response Times on the WWW。里面详细地整理了各种 HTML5 之前的可用的跨页面传递信息的方法,如 cookie、独立窗口、框架等。最令人惊讶的是还提到使用 Window Name 来保存信息,几年之后,这种使用 window.name 传递信息的方法才开始在前端工程师中流传开来。 […]

  48. 48. 谈谈我对 window.name 实现跨域的理解 « 加菲 Blog - 专注前端,相信自己

    […] (注2)使用 window.name 解决跨域问题:http://www.planabc.net/2008/09/01/window_name_transport/ Tags: […]

  49. 49. 跨域通信与实验 | zciii的博客

    […] http://www.planabc.net/2008/09/01/window_name_transport/ […]

  50. 50. 将在 HTTP 协议下用 XHR 调用后端接口的登录方式改为经过 SSL 加密的登录方式,这样的变动会带来什么问题,有哪些兼容主流浏览器的实现方案? - web前端开发 - 开发者问答

    […] 但是,目前国内IE6、IE7及相应内核的浏览器,依然占据着很大的浏览器市场份额,对于这部分浏览器,考虑采用window name传输的方式,来实现主页面和iframe的通信了,具体window name的实现细节,在这里就不再描述了,大家可以参考怿飞的博文《使用 window.name 解决跨域问题》http://www.planabc.net/2008/09/01/window_name_transport/, […]

发表评论

(必填)

(必填,会为您保密)

评论仅支持“a、abbr、strong、em、blockquote、code”几个简单的标签