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 传输技术的基本原理和步骤为:

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 属性。
(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 传输技术相比其他的跨域传输的一些优势:
- 它是安全的。也就是说,它和其他的基于安全传输的 frame 一样安全,例如 Fragment Identifier messaging (FIM)和 Subspace。(I)Frames 也有他们自己的安全问题,由于 frame 可以改变其他 frame 的 location,但是这个是非常不同的安全溢出,通常不太严重。
- 它比 FIM 更快,因为它不用处理小数据包大小的 Fragment Identifier ,并且它不会有更多的 IE 上的“机关枪”声音效果。它也比 Subspace 快,Subspace 需要加载两个 Iframe 和两个本地的 HTML 文件来处理一个请求。window.name 仅需要一个 Iframe 和一个本地文件。
- 它比 FIM 和 Subspace 更简单和安全。FIM 稍微复杂,而 Subspace 非常复杂。Subspace 也有一些额外的限制和安装要求,如预先声明所有的目标主机和拥有针对若干不同特殊主机的 DNS 入口。window.name 非常简单和容易使用。
- 它不需要任何插件(比如 Flash)或者替代技术(例如 Java)。
witter:
共有28 条评论
对于好文,我只会说沙发。
感觉js发展越来越畸形了。
好文~~
如果包含 w了indowname 参数
笔误~
既然用 YUI,那我就 lazy-load 了,更适合 lazy-bone。
收藏了~
赞下
介绍很详细,支持一下!
思路不错,赞一个,
不过提供的示例代码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指点一二
@哉崽 下面的序号针对你问题的序号
1、=== 是为了严格的匹配
2、如果没有proxyUrl,无法跨域访问(同源策略),也就是基本无法获取window.name,在原理中已经提到。
3、src未错误,只有再次转向时,需要用frame.contentWindow.location。
郁闷,跨不同域名,只有opera浏览器能获取到数据
其他浏览器都是 undefined
blank有用这个方法实现过跨异域的吗?不是二级域名,是不同域名的那种,如果有测试成功的示例代码,请发一份到我邮箱(joeke.cn@gmail.com),学些下,谢谢!
@哉崽 上面的 window.name 就是用来解决跨不同域名的方法呀–!汗的,包括后面在谈优点时提及的两种方式也是用来解决跨域问题的。至于opera,那是因为支持了HTML5中跨域传输
我就是复制了你的基本实现代码,只是去除了YUI部分,然后在跨域和非跨域两种环境下测试,搞出这些问题的。所以也很汗,代码发到你邮箱了,另外在外网服务器上放了不同域的测试数据,有时间帮忙看看下,谢谢!
问题解决啦,
我犯了个低级错误,把proxyUrl指到数据源的域下了,晕啊
另外,如果不在web服务环境下,直接本地建个目录测试,确实会发生一些有趣的问题,就像我第一个回帖描述的
没有服务,不用YUI
没有看出这种方式与动态脚本相比有什么好处。
这种方式也是只能解决get方式的请求,post请求并没有解决啊!
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);
});
@战栗 所有的A级浏览器均测试过,都可以的
@战栗 对于跨域数据之间完全互通,我还是推荐你使用 Julien Lecomte 提出的跨域方式。
@战栗 frame.onload 在ie下好像有问题, 但是有替代的解决办法:
http://blog.guitarbean.com/2008/08/iframe-onload-event-monitoring.html
window.name还可以用来缓存JS、CSS等静态文件
[...] 使用window.name解决跨域问题 [...]
貌似可以实现对多站用户的整合。。。
这里利用了url来实现数据的转移 蛮有意思
果然够巧,只是有个问题在FireFox会增加history的记录.
如果只有data域在控制下,提供给多个request页面服务对request域不够友好.
之前通过互传Hash值的简单跨域通信方式也会遇到扰乱FireFox浏览器history的问题.
不知道文中提到的FIM或者Subspace是否可以避免?
如果data域在控制下,并对外提供服务,是否可以通过参数接收request域的proxy页面地址,在data域页面执行后自行导入proxy页.这是不是18楼那张图表达的意思?
能不能也比较一下这种实现方式和on-demand script方案的各自特点和适合的应用场景?
safari下用js改变location.hash的时候页面会刷新,请教大虾有什么绝招不
使用window.name还是需要不同域的 service去配合,既然这样,用script方式加载,然后直接返回json数据不是更好?
[...] 之前在怿飞的博客上看到一篇关于window.name解决跨域问题的文章,给web前端的跨域问题提供了另外一种选择,但是里面的例子写得不是很简洁,这里从一个简单的例子,来说明该机制的应用。具体的机制说明可以参考http://www.planabc.net/2008/09/01/window_name_transport/ [...]
怿飞改天写篇FIM 和 Subspace 的文章吧…