On The Road ZJL

我的博客列表

2009年1月5日星期一

跨域的iframe操作 - ①兲的耐情 - JavaEye技术网站

跨域的iframe操作 - ①兲的耐情 - JavaEye技术网站

下面直接帖上网上找到的那篇文章(怕这个链接失效)
来自:http://hily.me/blog/2006/11/special-domain-data-fetch/。

引用
浏览器跨域获取特定域的数据的办法
以前看了几篇这方面的文章,但是都未能找到一个合适的解决方法。
获取同一个域的数据,可以通过XMLHTTP组件或IFRAME来实现,不存在跨域访问的权限问题,因此比较简单。
但如果要访问不同域的数据时,由于浏览器的安全设置,XMLHTTP没有权限获取数据,而IFRAME没有权限将获取的数据传递给父窗口,似乎没有其它解决办法。
在网上提到的方法,不外乎这两种:
1. 如果要获取的数据位于同一个根域但是不同子域时,可以在脚本中指定document.domain为父域。
2. 如果要获取的数据位于不同的根域时,则可以在服务器上写一个脚本作为代理,由服务器上的脚本获取不同域的数据,然后传递给在同一个域中的网页。
以上两种方法很容易便能想到,现在的问题是,如果要获取一个不同根域的数据时,该如何实现?

一般我们不会漫无目的地去网上获取数据,往往是从指定的服务器上获取数据,就像Google Maps、Google Adsense和referer这样的网页插件,一般都是以脚本的形式提供给用户使用的。这时,如果要求用户在自己的服务器上写个代理的话,易用性就要大打折扣。获取你会考虑为用户写好各种脚本的代理,PHP、ASP、Python、Perl……,但是,如果这台服务器不支持动态脚本,又该怎么办呢?
想了两天都没想通这个问题(除去上班时间其实不到两个小时),曾考虑过用浏览器的漏洞来实现,但是这样做不能长久,因此放弃。随后发现Google本地搜索的地图数据来自于mapabc.com,好家伙,他们是怎么办到的?
用FireFox的DOM查看器可以看到,地图区是一个IFRAME,难道是用IFRAME实现的?但是拖动地图时如何知道要下载哪些图片的?这些数据是一定要从服务器上获取的,难道放这些数据的服务器也是google.com域内的?应该不会这么麻烦。
随即找来Google Maps的API进行开刀,官网给的范例网页如下:


Java代码 复制代码

1. 2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3.
4.
5.
6. Google Maps JavaScript API Example
7.
9.
19.
20.
21.

22.
23.
24.
25.
26.
27. 把http://maps.google.com/maps?file=api&v=2&key=abcdefg下载下来看了一下,里面有一句:
28.
29.
30. GScript("http://maps.google.com/mapfiles/maps2.67.api.js");
31.
32. 这句就是用来加载地图操作库的,GScript函数定义为:
33.
34. function GScript(src) {document.write('');}

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">



Google Maps JavaScript API Example










把http://maps.google.com/maps?file=api&v=2&key=abcdefg下载下来看了一下,里面有一句:


GScript("http://maps.google.com/mapfiles/maps2.67.api.js");

这句就是用来加载地图操作库的,GScript函数定义为:

function GScript(src) {document.write('');}


顿悟……
网页内引用不同域的脚本并不会提示权限不足,对了,就是它没错!
这个方法其实我早应该想到的,可能是太久没玩javascript的缘故吧。不论是referer还是Google Analytics,要统计页面访问信息,都要用脚本来向服务器提交信息,只是它们只提交一次(Google Analytics有统计逗留时间,应该有好几次)。习惯性地,我把提交和下载数据分开了,所以不容易想到这个方法。
但是用document.write始终不是好办法,它会清除页面上原有的内容。改进的方法估计你已经想到了,可以参考一下我很早以前乱写的这篇文章《有效地组织页面中复杂的JavaScript脚本》里的思想。
继续分析Google Maps的代码,打开http://maps.google.com/mapfiles/maps2.67.api.js,里面有这样的代码:

Java代码 复制代码

1. ta.prototype.Hk=function(a,b){var c=this.ql(a);if(c){window.setTimeout(function(){b(c)},
2. 0)}else{var d="__cg"+Zf++ +(new Date).getTime();
3. try{if(this.qe==null){this.qe=document.getElementsByTagName("head")[0]}
4. var e=window.setTimeout(sd(d,b,a,403),15000);
5. if(!window.__geoStore){window.__geoStore={}}window.__geoStore[d]=Jf(this,d,b,e);
6. var f=document.createElement("script");f.type="text/javascript";
7. f.id=d;f.charset="UTF-8";
8. f.src=this.vl+"?q="+window.encodeURIComponent(a)+"&output=json&callback=__geoStore."+d+"&key="+this.Lh;
9. this.qe.appendChild(f)}catch(g){if(e){window.clearTimeout(e)}window.setTimeout(sd(d,
10. b,a,500),0)}}};

ta.prototype.Hk=function(a,b){var c=this.ql(a);if(c){window.setTimeout(function(){b(c)},
0)}else{var d="__cg"+Zf++ +(new Date).getTime();
try{if(this.qe==null){this.qe=document.getElementsByTagName("head")[0]}
var e=window.setTimeout(sd(d,b,a,403),15000);
if(!window.__geoStore){window.__geoStore={}}window.__geoStore[d]=Jf(this,d,b,e);
var f=document.createElement("script");f.type="text/javascript";
f.id=d;f.charset="UTF-8";
f.src=this.vl+"?q="+window.encodeURIComponent(a)+"&output=json&callback=__geoStore."+d+"&key="+this.Lh;
this.qe.appendChild(f)}catch(g){if(e){window.clearTimeout(e)}window.setTimeout(sd(d,
b,a,500),0)}}};



script节点也是动态创建的,这样就可以避免document.write产生的问题。


解决方法找到了,现在来简单测试一下:
1. 在本地服务器上新建一页面。
test.html:


Java代码 复制代码

1.
2.
3. Over-Domain Data Fetching Test Page
4.
19.
20.
21.

22.

23. My Name:

24. My Blog:
25.
26.



Over-Domain Data Fetching Test Page







My Name:

My Blog:




解释两个关键点:
1. lastScript用于存放上次建立的script节点的ID,在下次要再新建script节点时,要删除上次建立的节点,以免加载的脚本越来越多,占用过多的内存。
2. url后面加了一个数值d是为了防止浏览器缓存脚本数据,在本例中可以不加,但是如果脚本是由服务器动态生成的,那最好加上。
然后再建立两个javascript脚本用于测试:
alert.js:

Java代码 复制代码

1. alert('You can see me!');

alert('You can see me!');


info.js:

Java代码 复制代码

1. g('myname').value='Hily Jiang';
2. g('myblog').value='http://hily.iyi.cn/';

g('myname').value='Hily Jiang';
g('myblog').value='http://hily.iyi.cn/';


好了,把它们放在本地服务器的根目录下,敲入http://127.0.0.1/test.html,这样它和localhost就不在同一个域内了。
点击“Test Alert”,应该会弹出对话框显示You can see me!。
点击“Get My Info”,应该会在文本框中显示我的信息。
(以上测试页在IE 6.0和FireFox1 .5.0下测试通过。)

没有评论: