JavaScript中Ajax

时间:2021-03-22 08:19:33

Ajax技术,就是指:向服务器请求额外的数据而无须重新加载整个页面。其核心就是 XMLHttpRequest对象。(简称:XHR)

在这里,我们先讨论IE7及更高版本,以及FF,Opera,Chrome,Safari都支持的XHR。
创建一个XHR对象:
var xhr = new XMLHttpRequest();
XHR对象有几个方法,一个一个来:
open():

xhr.open("get","example.php",true);
这里有三个参数,get:表示ajax将以get请求的方式请求数据,example.php:是我们请求的URL,true:表示是以异步的方式发送请求。所谓异步,就是不用刷新整个页面,在后台浏览器后台发送请求。false:表示是同步的方式发送请求。
open()方法只是启动了一个请求,但是还未将此请求发出去。

send()
当执行:
xhr.send(null) 时,我们的请求就发送出去了。

当请求被处理完返回时,xhr对象会被填充一些相应的属性:
responseText:作为相应主体的文本。
responseXML:如果相应的数据类型是:text/xml 或者是 application/xml,这个属性中保存的是相应的XML DOM文档。
status:响应的HTTP状态
statusText:HTTP状态说明。

接收到响应时,应该先检查status的值,判断其是否成功。一般来说status的值为200表示成功,此时:responseText属性的内容已经被填充,responseXML属性的内容也已准备好。status的值为304表示请求的资源并没有更新,可以直接从浏览器的缓存中获取。这个也意味着响应是有效的。

如果请求是同步发送的,即:xhr.open("get","example.php",false),
那么我们紧接着写相应的处理代码就好:
xhr.open("get","example.php",false);
xhr.send(null);
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
    alert("successful: "+xhr.status); //成功
}else{
    alert("unsuccessful :"+xhr.status); //失败
}

如果请求是异步发送的,那么我们就要写一个回调函数来处理请求发送后接下来应该要做的事。
我们可以通过检测xhr.readyState属性,该属性表示请求/响应过程的当前活动阶段。其属性取值为:
0:未初始化,未调用open()方法
1:启动,已调用open()方法,未调用send()方法
2:发送,已调用send()方法,未接收到响应数据
3:接收,已接收到部分响应数据
4:完成,已接收到全部响应数据,并且可以在浏览器中使用。

所以我们可以这样来写:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
          if( (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                alert("successful : "+xhr.status);
           }else{
                alert("unsuccessful : "+xhr.status);
           }
    }
}
xhr.open("get","example.php",true);
xhr.send(null);

在这里为什么没有用this来替代xhr,是因为作用域的原因,用this来替代xhr,在有的浏览器中会执行失败。所以用xhr是一种比较可靠的方式。

可以使用DOM2级事件(addEventListener/removeEventListener ) 但是要注意浏览器支持。
在IE中,也可以使用IE的的事件处理程序(attachEvent/detachEvent)。不过用DOM0级事件是一种比较好的方式,因为它支持所有的浏览器。

可以调用xhr.abort()来取消异步请求,取消后,xhr对象停止触发事件,并且不允许访问任何有关响应的对象属性。
在终止请求后,最好解除掉xhr对象的引用,以释放内存空间。

在默认情况下:发送XHR请求的同时,会发送一下头部信息:
Accept:浏览器能处理的内容类型
Accept-Charset:浏览器能够显示的字符集,如:utf-8,gbk
Accept-Encoding:浏览器能够处理的压缩编码
Accept-Language:浏览器当前的语言,如:en,zh-Ch
Connection:浏览器与服务器之间的连接类型,如:keep-Alive
Host:发出请求页面所在的域
Cookie:当前页面设置的任何Cookie
Referer:发出请求页面的URI。
User-Agent:用户的浏览器代理

可以在发送前使用xhr.setRequestHeader()来增加一些头部信息:
xhr.open("get","example.php",true);
xhr.setRequestHeader("myheader":"myheader");
xhr.send(null);
但是最好不要修改默认的头部信息,即使有的浏览器支持这样做。

同样,在接收到响应之后,可以调用下列方法来获取头部信息:
xhr.getRequsetHeader("myvalue");
或者获取全部头部信息:
xhr.getAllRequestHeader();

GET请求:
GET请求通常带有一些查询的参数,比如:
xhr.open("get","example.php?page=1&search=2","true");

增加一个辅助函数来向当前的url末尾增加查询参数:
function addParam(url,name,value){
    url += (url.indexOf("?") == -1 ? "?":“&”);
    url += encodeURIComponent(name) + "=" +ecodeURIComponent(value);
    return url;
}

然后使用它:
var url = "example.php";
url = addParam(url,"page","1");
url = addParam(url,"search","2");
xhr.open("get",url,true);
xhr.send(null);

POST请求
可以使用XHR来模拟表单提交,需要修改xhr.open()方法为:
xhr.open("post","actionform.php",true);

另外需要增加头部信息:
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

然后是表单数据需要进行一些相应的处理:
var form = document.getElementsByTagName("form")[0];
xhr.send(serialize(form));
完整的模拟表单提交的POST请求就是:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
          if( (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                alert("successful : "+xhr.status);
           }else{
                alert("unsuccessful : "+xhr.status);
           }
    }
}
xhr.open("post","actionform.php",true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(document.getElementsByTagName("form")[0]);

与GET请求相比,POST请求消耗的资源多一些。

现代Web应用中频繁使用的是表单数据的序列化,可以使用一个新的函数来实现:
var formData = new FormData();
formData.append("name","caibin");

或者使用另一种方法:
var formData = new FormData(document.getElementsByTagName("form")[0]);

进度事件:
与XHR相关的还有一些进度的事件:
loadstart:在接收到响应数据的第一个字节触发
progress:在接收到响应期间持续不断触发
error:请求发生错误时触发
abort:因为调用abort()方法触发
load:接收到完整响应数据时触发
loadend:通信完成或者触发error,abort,load事件后触发(暂没有浏览器支持)

每个请求都从触发loadstart开始,接下来是多个progress事件,然后触发error,abort,load中的一个,最后触发loadend事件。
其中对于progress事件:
xhr的onprogress事件处理程序会接收到一个event对象,其中event.target 就是xhr,然后event有三个很有用的属性:
lengthComputable:表示进度事件是否可用
position:表示已经接收到的字节数
totalSize:表示根据Content-Length响应头确定的预期字节数
我们可以用这些信息创建一个进度条:
主要的程序代码如下:
HTML:
<div id="progress"></div>
<span id="progressnumber">0%</span>
<button id="ajax">send ajax</button>

JavaScript:
document.getElementById("ajax").addEventListener("click",function(){
var xhr = new XMLHttpRequest();
xhr.addEventListener("load",function(event){
console.log(event.target.status);
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
console.log("successful ! "+xhr.status);
}else{
console.log("unsuccessful ! "+xhr.status);
}
});
xhr.addEventListener("loadstart",function(){
console.log(xhr.status);
});
var pro = document.getElementById("progress"),
pronum = document.getElementById("progressnumber");
xhr.addEventListener("progress",function(event){
//在控制台打印进度过程
if(event.lengthComputable){
if(event.position < event.totalSize){
console.log("正在加载数据:"+event.position+"/"+event.totalSize);
}else{
console.log("加载完成: "+event.position+"/"+event.totalSize);
var realSize = Math.ceil(event.totalSize / 1024);
var dw = ["KB","MB","GB","TB"],i=0;
while(realSize > 1024){
realSize = Math.ceil(realSize / 1024);
i += 1;
}
console.log("一共是:"+realSize+dw[i]);
}
                                                //不断修改进度条宽度
pro.style.width = (event.position / event.totalSize) * 200 +"px";
                                                //不断修改进度显示数据
pronum.innerHTML = (event.position / event.totalSize) * 100 + "%";
}
});
xhr.addEventListener("error",function(){
alert(xhr.status+" 错误");
});
xhr.addEventListener("abort",function(){
alert("被终止了");
});
xhr.open("get","example.php",true);
xhr.send();
});