发新话题
打印

Javascript跨域和Ajax跨域解决方案

Javascript跨域和Ajax跨域解决方案

一个项目中需要ajax跨域取得数据,如果是在本域中确实没有问题,但是放到二级域和其他域下浏览器直接就弹出提示框:“该页正在访问其控制范围之外的数据,这有些危险,是否继续"


1.什么引起了ajax跨域不能的问题
ajax本身实际上是通过XMLHttpRequest对象来进行数据的交互,而浏览器出于安全考虑,不允许js代码进行跨域操作,所以会警告。

2.有什么完美的解决方案么?
没有。解决方案有不少,但是只能是根据自己的实际情况来选择。

具体情况有:
一、本域和子域的相互访问: www.aa.com和book.aa.com
二、本域和其他域的相互访问: www.aa.com和www.bb.com 用 iframe
三、本域和其他域的相互访问: www.aa.com和www.bb.com 用 XMLHttpRequest访问代理
四、本域和其他域的相互访问: www.aa.com和www.bb.com 用 JS创建动态脚本


解决方法
一、如果想做到数据的交互,那么www.aa.com和book.aa.com必须由你来开发才可以。可以将book.aa.com用iframe添加到 www.aa.com的某个页面下,在www.aa.com和iframe里面都加上document.domain = "aa.com",这样就可以统一域了,可以实现跨域访问。就和平时同一个域中镶嵌iframe一样,直接调用里面的JS就可以了。(这个办法我没有尝 试,不过理论可行)


二、当两个域不同时,如果想相互调用,那么同样需要两个域都是由你来开发才可以。用iframe可以实现数据的互相调用。解决方案就是用 window.location对象的hash属性。hash属性就是http://domian/web/a.htm#dshakjdhsjka 里面的#dshakjdhsjka。利用JS改变hash值网页不会刷新,可以这样实现通过JS访问hash值来做到通信。不过除了IE之外其他大部分浏 览器只要改变hash就会记录历史,你在前进和后退时就需要处理,非常麻烦。不过再做简单的处理时还是可以用的,具体的代码我再下面有下载。大体的过程是 页面a和页面b在不同域下,b通过iframe添加到a里,a通过JS修改iframe的hash值,b里面做一个监听(因为JS只能修改hash,数据 是否改变只能由b自己来判断),检测到b的hash值被修改了,得到修改的值,经过处理返回a需要的值,再来修改a的hash值(这个地方要注意,如果a 本身是那种查询页面的话比如http://domian/web/a.aspx?id=3,在b中直接parent.window.location是无 法取得数据的,同样报没有权限的错误,需要a把这个传过来,所以也比较麻烦),同样a里面也要做监听,如果hash变化的话就取得返回的数据,再做相应的 处理。


三、这种情形是最经常遇到的,也是用的最多的了。就是www.aa.com和www.bb.com你只能修改一个,也就是另外一个是别人的,人家告诉你你 要取得数据就访问某某连接参数是什么样子的,最后返回数据是什么格式的。而你需要做的就是在你的域下新建一个网页,让服务器去别人的网站上取得数据,再返 回给你。domain1下的a向同域下的GetData.aspx请求数据,GetData.aspx向domain2下的 ResponseData.aspx发送请求,ResponseData.aspx返回数据给GetData.aspx, GetData.aspx再返回给a,这样就完成了一次数据请求。GetData.aspx在其中充当了代理的作用。具体可以看下我的代码。


四、这个和上个的区别就是请求是使用<script>标签来请求的,这个要求也是两个域都是由你来开发才行。原理就是JS文件注入,在本域内 的a 内生成一个JS标签,它的SRC指向请求的另外一个域的某个页面b,b返回数据即可,可以直接返回JS的代码。因为script的src属性是可以跨域 的。具体看代码,这个也比较简单。



[size=+0]总结:
第一种情况:域和子域的问题,可以完全解决交互。
第二种情况:跨域,实现过程非常麻烦,需要两个域开发者都能控制,适用于简单交互。
第三种情况:跨域,开发者只控制一个域即可,实现过程需要增加代理取得数据,是常用的方式。
第四种情况:跨域,两个域开发者都需要控制,返回一段js代码。

===================
A域有页面a.html,其中有iframe包含B域的页面b.html,现在要通过a.html上的一个按钮,来把a.html页面上一个文本框的值传递到b.html页面的文本框。
注: 这里b.html是html网页,不能接收其他网站post过来的值,所以不能用直接post的方法来传值,但是,如果接收页面是b.aspx或者 b.asp 呢,那不是可以直接post了么?答案是肯定的,确实可以,但是b.asp或b.aspx必须要刷新,才可以,如何能不刷新的动态改变接收页的元素或者值 呢?(IE的本地项目是可以实现跨域访问的,但是外网的跨域访问默认是被拒绝的。FireFox本地项目以及外网的跨域访问都是被拒绝的。)
原理:
浏览器禁止跨域数据访问,但是浏览器并没有禁止跨域跨框架的post传值。我们可以在A域,post到B域的某个页面的框架中,然后通过B域的框架页来实现本域内的数据访问。这其实是html应用中的一个小技巧,并没有用到其他高深的知识就实现了跨域的数据提交。
方法:
在B域中添加两个页面,来实现跨域的数据访问,post.aspx和main.aspx。
页面关系如下,A域的a.html包含一个框架,框架页地址是B域的main.aspx,main.aspx是一个框架集包含两个框架,(frmMain)b.html 和(frmPost)post.aspx.
A域的a.html:
<form action="http://www.b**.com/post.aspx" method="post" target="frmPost">
<input id="cmd" type="text" size="20">
<input type="submit">
</form>
<iframe src="http://www.b**.com/main.aspx"></iframe>
B域的main.aspx:
<frameset rows="*,0" frameborder="no" border="0" framespacing="0">
<frame src="b.html" name="frmMain">
<frame src="post.aspx" name="frmPost">
</frameset>

我们先把要传递到B域的数据保存到a.html的form中,然后post到B域的post.aspx.
这时post.aspx接收到值,然后执行本域内的父框架访问b.html。
string cmd = Request.Form["cmd"];
if (null != cmd && string.Empty != cmd)
{
        Response.Write("<script language=\"JavaScript\" for=\"window\" event=\"onload\"> if (parent && parent.frames[\"frmMain\"]) { 这里添加控制b.html的执行代码} </script> ");
}
不难发现,这里利用跳跃跨frame(即中间跃过了一层frame)的方法,来实现跨域的数据访问。即post到frame的子frame里面。
后记:
这个例子不过是一些特殊的情况下跨域访问的解决方案,也许对你会有所帮助。因为方法简单,应用也就有很多局限性。(不过偶倒是觉得这样很象ajax哦,页面没有刷新,同样完成了一次服务端的数据处理^o^)。
相关网文资料:
web应用的跨域访问解决方案
做过跨越多个网站的Ajax开发的朋友都知道,如果在A网站中,我们希望使用Ajax来获得B网站中的特定内容,如果A网站与B网站不在同一个域中,那么就出现了跨域访问问题。Ajax的跨域访问问题是现有的Ajax开发人员比较常遇到的问题。
IE对于跨域访问的处理是,弹出警告框,提醒用户。如果用户将该网站纳入可信任网站,或者调低安全级别,那么这个问题IE就不会在提醒你。
FireFox等其它非微软的浏览器遇到跨域访问,则解决方案统一是拒绝访问。
有 人说,IE是主流浏览器,只要它能正常使用就好了。此言差已,IE虽然能够处理,但是是有前提的,要么用户不厌其烦地在页面弹出警告框之后点击是(点击否 就不执行该Ajax调用了),要么用户将该网站纳入可信任站点。这两种做法,在企业管理系统的应用中倒是比较常见,因为系统管理员可以以行政手段保证用户 的行为。但是对于互联网上的网站或者门户开发,这种做法则不行。
最近遇到了这个问题,需要在跨域访问结束之后完成使主窗口出现一些特效,搜索了一些资料,通过不断尝试以及在不同浏览器中进行兼容性测试,找到了几个可行的方案:
1、 Web代理的方式。即用户访问A网站时所产生的对B网站的跨域访问请求均提交到A网站的指定页面,由该页面代替用户页面完成交互,从而返回合适的结果。此 方案可以解决现阶段所能够想到的多数跨域访问问题,但要求A网站提供Web代理的支持,因此A网站与B网站之间必须是紧密协作的,且每次交互过程,A网站 的服务器负担增加,且无法代用户保存session状态。
2、on-Demand方式。MYMSN的门户就用的 这种方式,不过MYMSN中不涉及跨域访问问题。动态控制script标记的生成,通过修改script标记的src属性完成对跨域页面的调用。此方案存 在的缺陷是,script的src属性完成该调用时采取的方式时get方式,如果请求时传递的字符串过大时,可能会无法正常运行。不过此方案非常适合聚合 类门户使用。
3、iframe方式。查看过醒来在javaeye上的一篇关于跨域访问的帖子,他提到自己已经用 iframe的方式解决了跨域访问问题。数据提交跟获取,采用iframe这种方式的确可以了,但由于父窗口与子窗口之间不能交互(跨域访问的情况下,这 种交互被拒绝),因此无法完成对父窗口效果的影响。
(偶找到了该文,补充一下地址:http://www.javaeye.com/topic/15641
4、 用户本地转储方式:IE本身依附于windows平台的特性为我们提供了一种基于iframe,利用内存来“绕行”的方案,即两个window之间可以在 客户端通过windows剪贴板的方式进行数据传输,只需要在接受数据的一方设置Interval进行轮询,获得结果后清除Interval即可。FF的 平台独立性决定了它不支持剪贴板这种方式,而以往版本的FF中存在的插件漏洞又被fixed了,所以FF无法通过内存来完成暗渡陈仓。而由于文件操作FF 也没有提供支持(无法通过Cookie跨域完成数据传递),致使这种技巧性的方式只能在IE中使用。
5、我自己 用于解决这类问题的方式:结合了前面几种方式,在访问A网站时,先请求B网站完成数据处理,再根据返回的标识来获得所需的结果。这种方法的缺点也很明 显,B网站的负载增大了。优点,对session也实现了保持,同时A网站与B网站页面间的交互能力增强了。最重要的一点,这种方案满足了我的全部需要。
总结一下,以上方案中可选择的情况下,我最推荐on-Demand方式,在不需要提交大量数据的情况下,这种方式能够解决您的大部分问题。

===============================
本地站:http://www.somedomain.com
目标站:http://bbs.somedomain.com
解决方法 :
1. 在目标站 document.domain = 'somedomain.com';
并建立一个ajax.html,引用ajax方法 (大家都叫它服务中介)
然后创建一个ajax对象 var webreq = new Ajax();
2. 在本地站 document.domain = 'somedomain.com';
用iframe引用目标站的ajax.html.

详细方法:
1. 本地站的一个页面(Test.html)
html:
<html>
<head>
<script type='text/javascript'>document.domain='somedomain.com';</script>
<script type='text/javascript'>
function getAjax()
{
var spn = document.getElementById('spninfo');
var bbsWin = document.getElementById('ifrWindow').contentWindow;
var Ajax = bbsWin.webreq;
Ajax.Config.Result = 'TestAjaxCross';
Ajax.Config.returnType = 'Content';
Ajax.ActionPost('http://bbs.somedomain.com/doAjax.aspx',spn);
}
</script>
</head>
<body>
<span id='spninfo' />
<input type='button' id='ajaxBtn' onclick='getAjax' value='Get' />
</body>
<iframe id='ifrWindow' src='http://bbs.somedomain.com/ajax.html' style='display:none;'></iframe>
</html>

/////////////////////////////////////////////////////////////////////////////

2. 目标站的ajax.html
Html:
<html>
<head>
<script type='text/javascript'>document.domain='somedomain.com';</script>
<script type='text/javascript' scr='http://bbs.somedomain.com/ajaxMethod.js'></script>
<script type='text/javascript'>var webreq = new Ajax();</script>
</head>
<body>
</body>
</html>

----------------------------------------------------------------
目标站: doAjax.aspx
html为空
doAjax.aspx.cs
代码(Page_Load)
Response.Write("<zwd><content>看到效果了么?</content></zwd>");
return;

=====================================================

AjaxMethod.js 代码

////////////////////Ajax////////////Class///////////////////
//Power By Gloot CopyRight @2006
//Edit Section
//Blog http://blog.sina.com.cn/tecz
//QQ 345268267
///////////////////////////////////////////////////////////
var Try = {
these: function() {
var returnValue;
for (var i = 0; i < arguments.length; i++) {
var lambda = arguments;
try {
returnValue = lambda();
break;
} catch (e) {}
}
//alert(123);
return returnValue;
}
}
function grr(rp) {
if(RegExp.$1)/(.*)/.exec("");
var re=new RegExp("<result>(.*)<\/result>");
re.exec(rp);
if(RegExp.$1) return RegExp.$1;
return "";
}
function crr(rp) {
if(RegExp.$1)/(.*)/.exec("");
var re=new RegExp("<content>(.*)<\/content>");
re.exec(rp);
if(RegExp.$1) return RegExp.$1;
return "";
}
var Ajax = function() {}
//var xhr ;这样定义不行
Ajax.prototype.Init = function(){
return Try.these(
function() {return new ActiveXObject("Msxml2.XMLHTTP")},
function() {return new ActiveXObject("Microsoft.XMLHTTP")},
function() {return new XMLHttpRequest()}
) || false;
}
Ajax.prototype.Config = {
Result:"",
SucInfo:"",
FaildInfo:"",
Url:"",
returnType:"Compare", //输入Compare是比较返回的字符是否一致,要指定Result值,//其他返回内容
ExecFunc:function(ty){
if (typeof ExecResult == 'function')
ExecResult(ty);
},
sendData:""
}
var aj = new Ajax();
Ajax.prototype.Action = function(url) { //同步
url = url + '&e='+Math.random();
var xhr = aj.Init();
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.FuncResult(aj.Config.Spn,xhr);
}
}
}
xhr.open("POST",url,false);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(null);
}
Ajax.prototype.Actionfor = function(url,spn) { //异步
url = url + '&e='+Math.random();
var xhr = aj.Init(); //这样写是为了多异步执行
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.FuncResult(spn,xhr);
}
}
}
xhr.open("GET",url,true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(null);
}
Ajax.prototype.ActionAlert = function(url) //执行alert提示框的同步
{
url = url + '&e='+Math.random();
var xhr = aj.Init(); //这样写是为了多异步执行
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.AlertResult(xhr);
}
}
}
xhr.open("GET",url,false);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(null);
}
Ajax.prototype.ActionPost = function(url,spn) //同步 send
{
url = url + '?e='+Math.random();
var xhr = aj.Init(); //这样写是为了多异步执行
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.postResult(spn,xhr);
}
}
}

try {
if (netscape.security.PrivilegeManager.enablePrivilege)
{
netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
}
}catch(e) {};

xhr.open("POST",url,true);

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//xhr.setRequestHeader("Content-Length",pars.length);
xhr.setRequestHeader("Connection", "open");
xhr.send(Webreq.Config.sendData);
}
Ajax.prototype.GetJSONData = function(url,spn)
{
url = url + '?e='+Math.random();
var xhr = aj.Init(); //这样写是为了多异步执行
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
aj.jsonResult(spn,xhr);
}
}
}

xhr.open("POST",url,true);

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//xhr.setRequestHeader("Content-Length",pars.length);
xhr.setRequestHeader("Connection", "open");

xhr.send(null);
}
Ajax.prototype.onResult = function(v) {
return v==aj.Config.Result;
}
Ajax.prototype.FuncResult = function(spn,xhr)
{ //alert(spn.id);
if (aj.Config.returnType == 'Compare')
{
if (aj.onResult(grr(xhr.responseText)))
{
spn.innerHTML = aj.Config.SucInfo;
aj.Config.ExecFunc(aj.Config.Result);
if (aj.Config.Url!='')
{
window.location.href = aj.Config.Url;
}
}
else
{
spn.innerHTML = aj.Config.FaildInfo;
}
}
else
{
spn.innerHTML = crr(xhr.responseText);
aj.Config.ExecFunc(aj.Config.Result);
}
}

Ajax.prototype.AlertResult = function(xhr)
{
if (aj.Config.returnType=='Compare')
{
if (aj.onResult(grr(xhr.responseText)))
{
alert(aj.Config.SucInfo);
aj.Config.ExecFunc(aj.Config.Result);
}
else
{
alert(aj.Config.FaildInfo);
}
}
else
{
alert(crr(xhr.responseText));
aj.Config.ExecFunc(aj.Config.Result);
}
}
Ajax.prototype.postResult = function(spn,xhr)
{
if (aj.Config.returnType == 'Compare')
{
if (aj.onResult(grr(xhr.responseText)))
{
spn.innerHTML = aj.Config.SucInfo;
aj.Config.ExecFunc(aj.Config.Result);
}
else
{
spn.innerHTML = aj.Config.FaildInfo;
}
}
else
{
spn.innerHTML = crr(xhr.responseText);
aj.Config.ExecFunc(aj.Config.Result);
}
}
Ajax.prototype.jsonResult = function(spn,xhr)
{
var jsonstr = xhr.responseText;
var json = eval("return " + jsonstr);

//get data json.something json:{username:"123",content:""};

aj.Config.ExecFunc(aj.Config.Result);
}
///////////////////////////////////////////////////////////////
var WebServices = function() {}
WebServices.Config = {
}

TOP

IFRAME的方法简单示范如下:

1. http://domain1/TestCross.html:

<html>
<head>
<script language="javascript" type="text/javascript">
var url = "http://domain2/TestCross.html"
var oldHash = null;
var timer = null;

function getHash()
{
var hash = window.location.hash;
if ((hash.length >= 1) && (hash.charAt(0) == '#'))
{
hash = hash.substring(1);
}

return hash;
}
function sendRequest()
{
var d = document;
var t = d.getElementById('request');
var f = d.getElementById('alienFrame');
f.src = url + "#" + t.value + "<br/>" + new Date();
}

function setDivHtml(v)
{
var d = document;
var dv = d.getElementById('response');
dv.innerHTML = v;
}

function idle()
{
var newHash = getHash();

if (newHash != oldHash)
{
setDivHtml(newHash);
oldHash = newHash;
}

timer = window.setTimeout(idle, 100);
}

function window.onload()
{
timer = window.setTimeout(idle, 100);
}
</script>
</head>
<body>

请求:<input type="text" id="request"> <input type="button" value="发送" onclick="sendRequest()" /><br/>
回复:<div id="response"></div>

<iframe id="alienFrame" src="http://domain2/TestCross.html"></iframe>

</body>
</html>

2. http://domain2/TestCross.html:

<html>
<head>
<script language="javascript" type="text/javascript">
var url = "http://domain1/TestCross.html"
var oldHash = null;
var timer = null;

function getHash()
{
var hash = window.location.hash;
if ((hash.length >= 1) && (hash.charAt(0) == '#'))
{
hash = hash.substring(1);
}

return hash;
}
function sendRequest()
{
var d = document;
var t = d.getElementById('request');
var f = parent;
//alert(f.document); //试着去掉这个注释,你会得到“Access is denied”
f.location.href = url + "#" + t.value + "<br/>" + new Date();
}

function setDivHtml(v)
{
var d = document;
var dv = d.getElementById('response');
dv.innerHTML = v;
}

function idle()
{
var newHash = getHash();

if (newHash != oldHash)
{
setDivHtml(newHash);
oldHash = newHash;
}

timer = window.setTimeout(idle, 100);
}

function window.onload()
{
timer = window.setTimeout(idle, 100);
}
</script>
</head>
<body>

请求:<input type="text" id="request"> <input type="button" value="发送" onclick="sendRequest()" /><br/>
回复:<div id="response"></div>

</body>
</html>

两个网页基本相同,第一个网页内嵌一个IFRAME,在点击“发送”按钮后,会将文本框里的内容通过hash fragment传给IFRAME。点击IFRAME里的“发送”按钮后,它会将文本框里的内容通过hash fragment传给父窗口。因为是只改动了hash fragment,浏览器不会重新load网页内容,这里使用了一个计时器来检测URL变化,如果变化了,就更新其中一个div的内容 。

这个方法是不是个安全漏洞?考虑到微软的Windows Live都在使用这个方法,估计不是,。这个方法是不是很安全?考虑到这个方法只有在2个网站协作的情形才能成功,安全问题好像不是很大,除非其中涉及的网站本身有XSS的问题。

本文来自CSDN博客,转载请标明出处:

TOP

看看baidu是如何AJAX跨域的
最近做个人网站遇到AJAX跨子域名的问题。

偶尔看到baidu的通行证处理都是在二级域名passport.baidu.com中处理的,
但是baidu很多地方登录都好像是用ajax处理的,他是怎么做的呢?研究了一下,发现一个小技巧。
不防让大家也借鉴一下。
http://zhidao.baidu.com/ 未登录用户回答问题时会用iframe调用http://zhidao.baidu.com/userlogin.html
userlogin.html有下面的javascript


<SCRIPT LANGUAGE="JavaScript">
document.domain="baidu.com";
<!--
function G(id){if(typeof(id)=="string"){return document.getElementById(id);}return id;}
function showInfo(obj){
    if(obj.checked == true){
        G("memInfo").style.display="block";
    }else{
        G("memInfo").style.display="none";
    }
}
function request(id,url){
    oScript = document.getElementById(id);
    var head = document.getElementsByTagName("head").item(0);
    if (oScript) {
        head.removeChild(oScript);
    }
    oScript = document.createElement("script");
    oScript.setAttribute("src", url);
    oScript.setAttribute("id",id);
    oScript.setAttribute("type","text/javascript");
    oScript.setAttribute("language","javascript");
    head.appendChild(oScript);
    return oScript;
}
var loginTimer=null;
var loginState=-1;
var tryTime=0;
function PSP_ik(isOk){
    if(isOk==0){
        G("errorInfo").style.display="none";
        loginState=1;
        if(parent.loginSuccess){
            parent.Pop.hide();
            parent.loginSuccess();
        }
    }
    else
    {
        loginFalse();
    }
}

function loginFalse(){
    loginState=0;
    var err=G("errorInfo");
    err.innerHTML="用户名或密码错误,请重新登录";
    err.style.display="block";
    G("username").focus();
    tryTime++;
    if(tryTime>1){
        onLoginFailed();
    }
}
function onLoginFailed(){
    if(parent.onLoginFailed){
        parent.Pop.hide();
        parent.loginFailed();
    }else{
        document.login.u.value=escape("http://zhidao.baidu.com/q"+parent.location.search);
        doucment.login.submit();
    }
        
}
function loginTimeout(){
    if(loginState==-1){
        var err=G("errorInfo");
        err.innerHTML="操作超时,请重新登录";
        err.style.display="block";
        G("username").focus();
    }
}
function userLogin(){
    var username=G('username').value;
    var password=G('password').value;
    var memPassport=G('memPassport').checked?"on":"off";
    if(username.length <=0||password.length <=0){G("username").focus();return false;}
    var url = 'https://passport.baidu.com/?logt&tpl=ik&t=0&keyname=ik&mem_pass='+memPassport+'&username='+username + '&loginpass=' +escape(password)+ '&s=' + (new Date()).getTime();
    loginState=-1;
    var login=request("loginScript",url);
    loginTimer = setTimeout(loginTimeout, 5000);

}
window.onload=function(){
    document.loginForm.username.focus();   
    document.getElementById("username").focus();
}
//-->
</SCRIPT>

我们可以看到request方法处理异步请求使用动态往head中添加script而不是用xmlhttp发送get请求。
妙就妙在这。我们知道调用javascript是没有域的限制的。当加载完成时一样会执行。

当然请求参数只能通过拼url的方式了。
url通过服务器处理后直接输出loginFalse()或者PSP_ik();
非常优雅的解决了跨域的问题。

这让我们想到了用iframe当ajax上传文件一样异曲同工。
如果不需要服务器反馈,google的点击计数用new img().src=...;

当然baidu这段脚本中还有一些小的技巧也值得我们学习。

TOP

发新话题