Ajax(Asynchronous JavaScript and XML)是使用JavaScript脚本,借助XMLHttpRequest插件,在客户端与服务器端之间实现异步通信的一种方法。2005年2月,Ajax第一次正式出现,从此以后Ajax成为JavaScript发起HTTP异步请求的代名词。2006年,W3C发布了Ajax标准,Ajax技术开始快速普及。
代码测试环境:Windows(运行系统)+Apache(服务器)+PHP(脚本)。
XMLHttpRequest是客户端的一个API,它为浏览器与服务器通信提供了一个便捷通道。现代浏览器都支持XMLHttpRequest API,如IE7+、Firefox、Chrome、Safari和Opera等。30.1.1 定义XMLHttpRequest对象
使用XMLHttpRequest插件的第一步:创建XMLHttpRequest对象,具体方法如下:
var xhr = new XMLHttpRequest();
提示:IE 5.0版本开始以ActiveX组件形式支持XMLHttpRequest,IE 7.0版本开始支持标准化XMLHttpRequest。不过,所有浏览器实现的XMLHttpRequest对象都提供相同的接口和用法。
【示例】使用工厂模式对定义的XMLHttpRequest对象进行封装,这样只要调用createXHR()方法就可以返回一个XMLHttpRequest对象:
在上面示例中,首先定义一个数组,收集各种创建XMLHttpRequest对象的函数。第一个函数是标准用法,其他函数主要针对IE浏览器的不同版本尝试创建ActiveX对象。其次设置变量xhr为null,表示为空对象。最后遍历工厂内所有函数并尝试执行它们。为了避免发生异常,把所有调用函数放在try中执行,如果发生错误,则在catch中捕获异常,并执行continue命令,返回继续执行,避免抛出异常。如果创建成功,则中止循环,返回XMLHttpRequest对象。
使用XMLHttpRequest对象的open()方法可以建立一个HTTP请求,用法如下所示:
xhr.open(method, url, async, username, password);
其中xhr表示XMLHttpRequest对象,open()方法包含5个参数,简单说明如下:
建立连接后,可以使用send()方法发送请求,用法如下:
xhr.send(body);
参数body表示将通过该请求发送的数据,如果不传递信息,可以设置为null或者省略。
发送请求后,可以使用XMLHttpRequest对象的responseBody、responseStream、responseText或responseXML属性等待接收响应数据。
【示例】简单演示实现异步通信的方法:
var xhr = createXHR(); //实例化XMLHttpRequest对象
xhr.open("GET","server.txt", false); //建立连接,要求同步响应
xhr.send(null); //发送请求
console.log(xhr.responseText); //接收数据
在服务器端文件(server.txt )中输入下面的字符串:
Hello World //服务器端脚本
在浏览器控制台会显示“Hello World”的提示信息,该字符串是从服务器端响应的字符串。
发送GET请求简单、方便,适用于简单字符串,不适用于大容量或加密数据。实现方法:将包含查询字符串的URL传入XMLHttpRequest对象的open()方法,设置第一个参数值为GET即可。服务器能够通过查询字符串接收用户信息。
【示例】以GET方式向服务器传递一条信息callback=functionName:
<input name="submit" type="button" id="submit" value="向服务器发出请求" />
<script>
window.onload = function(){ //页面初始化
var b = document.getElementsByTagName("input")[0];
b.onclick = function(){
var url = "server.php?callback=functionName" //设置查询字符串
var xhr = createXHR(); //实例化XMLHttpRequest对象
xhr.open("GET",url, false); //建立连接,要求同步响应
xhr.send(null); //发送请求
console.log(xhr.responseText); //接收数据
}
}
</script>
在服务器端文件(server.php)中输入下面的代码,获取查询字符串中callback的参数值,并把该值响应给客户端:
<?php
echo $_GET["callback"];
?>
在浏览器中预览页面,当单击“提交”按钮时,在控制台显示传递的参数值。
提示:查询字符串通过问号(?)作为前缀附加在URL的末尾,发送数据是以连字符(&)连接的一个或多个名/值对。
POST请求允许发送任意类型、长度的数据,多用于表单提交,以send()方法进行传递,而不以查询字符串的方式进行传递。POST字符串与GET字符串的格式相同,格式如下:
send("name1=value1&name2=value2…");
【示例】使用POST方法向服务器传递数据:
window.onload = function(){ //页面初始化
var b = document.getElementsByTagName("input")[0];
b.onclick = function(){
var url = "server.php" //设置请求的地址
var xhr = createXHR(); //实例化XMLHttpRequest对象
xhr.open("POST",url, false); //建立连接,要求同步响应
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
//设置为表单方式提交
xhr.send("callback=functionName"); //发送请求
console.log(xhr.responseText); //接收数据
}
}
在open()方法中,设置第一个参数为POST,然后使用setRequestHeader()方法设置请求消息的内容类型为“application/x-www-form-urlencoded”,它表示传递的是表单值,一般使用POST发送请求时都必须设置该选项,否则服务器无法识别传递过来的数据。
在服务器端设计接收POST方式传递的数据,并进行响应:
<?php
echo $_POST["callback"];
?>
GET和POST方法都是以串行格式化的字符串发送数据,主要形式有以下两种:
例如,定义一个包含3个名/值对的对象数据:
{ user:"ccs8", pass: "123456", email: "[email protected]" }
转换为串行格式化的字符串表示为:
'user="ccs8"&pass="123456"&email="[email protected]"'
例如,定义一组信息,包含多个对象类型的元素:
[{ name:"user", value:"css8" }, { name:"pass", value:"123456" },{ name:"email", value:"[email protected]" } ]
转换为串行格式化的字符串表示为:
'user="ccs8"& pass="123456"& email="[email protected]"'
【示例】为了方便开发,下面演示如何定义一个工具函数,把JavaScript对象或数组对象转换为串行格式化字符串并返回,这样就不需要手动转换了:
使用XMLHttpRequest对象的readyState属性可以实时跟踪响应状态。当该属性值发生变化时,会触发readystatechange事件,调用绑定的回调函数。readyState属性值说明如下表所示:
如果readyState属性值为4,说明响应完毕,那么就可以安全读取响应的数据。注意,考虑到各种特殊情况,更安全的方法是,同时监测HTTP状态码,只有当HTTP状态码为200时,才说明HTTP响应顺利完成。
【示例】修改请求为异步响应请求,然后通过status属性获取当前的HTTP状态码。如果readyState属性值为4,且status(状态码)属性值为200,则说明HTTP请求和响应过程顺利完成,这时可以安全、异步地读取数据:
使用XMLHttpRequest对象的abort()方法可以中止正在进行的请求,用法如下:
xhr.onreadystatechange = function(){}; //清理事件响应函数
xhr.abort(); //中止请求
提示:在调用abort()方法前,应先清除onreadystatechange事件处理函数,因为IE和Mozilla在请求中止后也会激活事件处理函数。因为将onreadystatechange属性设置为null,IE会发生异常,所以可以为它设置一个空函数。
XMLHttpRequest对象通过responseText、responseBody、responseStream或responseXML属性XMLHttpRequest对象获取响应信息,它们都是只读属性,说明如下表所示:
在实际应用中,一般将格式设置为XML、HTML、JSON或其他纯文本格式。具体使用哪种响应格式,可以参考下面几条原则。
【示例1】在服务器端创建一个简单的XML文档:
<the>XML数据the >
然后,在客户端进行如下请求:
在上面代码中,使用XML DOM的getElementsByTagName()方法获取the节点,然后再定位第一个the节点的子节点内容。此时如果继续使用responseText属性读取数据,则会返回XML源代码字符串。
【示例2】可以使用服务器端脚本生成XML结构数据:
<the>XML数据the >'; //输出XML
?>
设计响应信息为HTML字符串,然后使用DOM的innerHTML属性把获取的字符串插入网页中。
【示例】在服务器端设计响应信息为HTML结构代码:
<table border="1" width="100%">
<tr><td>RegExp.exec()</td><td>通用的匹配模式</td></tr>
<tr><td>RegExp.test()</td><td>检测一个字符串是否匹配某个模式</td></tr>
</table>
在客户端接收响应信息:
注意:在某些情况下,HTML字符串可能为客户端解析响应信息节省一些JavaScript脚本,但是也带来一些问题。
设计响应信息为JavaScript代码,与JSON数据不同,它是可执行的命令或脚本。
【示例】在服务器端请求文件中包含下面一个函数:
function(){
var d = new Date()
return d.toString();
}
在客户端执行下面的请求:
注意:使用eval()方法时,在字符串前后附加两个小括号:一个是包含函数结构体的,一个是表示调用函数的。不建议直接使用JavaScript代码作为响应格式,因为它不能够传递更丰富的信息,同时JavaScript脚本极易引发安全隐患。
使用responseText可以获取JSON格式的字符串,然后使用eval()方法将其解析为本地JavaScript脚本,再从该数据对象中读取信息。
【示例】在服务器端请求文件中包含下面JSON数据:
{user:"ccs8",pass: "123456",email:"[email protected]"}
然后在客户端执行下面的请求。把返回JSON字符串转换为对象,然后读取属性值:
注意:eval()方法在解析JSON字符串时存在安全隐患。如果JSON字符串中包含恶意代码,在调用回调函数时可能会被执行。解决方法:先对JSON字符串进行过滤,屏蔽敏感或恶意代码。也可以访问http://www.json.org/json2.js下载JavaScript版本解析程序。如果确信所响应的JSON字符串是安全的,没有被人恶意攻击,那么可以使用eval()方法解析JSON字符串。
对于简短的信息,可以使用纯文本格式进行响应。但是纯文本信息在传输过程中容易丢失,且没有办法检测信息的完整性。
【示例】服务器端响应信息为字符串“true”,则可以在客户端这样设计:
HTTP请求和响应都包含一组头部消息,获取和设置头部消息可以使用XMLHttpRequest对象的下面两个方法:
【示例】下面将获取HTTP响应的所有头部消息:
var xhr = createXHR();
var url = "server.txt";
xhr.open("GET", url, true);
xhr.onreadystatechange = function (){
if ( xhr.readyState == 4 && xhr.status == 200 ) {
console.log(xhr.getAllResponseHeaders()); //获取头部消息
}
}
xhr.send(null);
如果要获取指定的某个首部消息,可以使用getResponseHeader()方法,参数为获取首部的名称。例如,获取Content-Type首部的值,则可以按以下方式设计:
console.log(xhr.getResponseHeader("Content-Type"));
除了可以获取这些头部消息外,还可以使用setRequestHeader()方法在发送请求中设置各种头部消息。用法如下:
xhr.setRequestHeader("Header-name", "value");
其中Header-name表示头部消息的名称,value表示消息的具体值。例如,使用POST方法传递表单数据,可以设置如下头部消息:
xhr.setRequestHeader("Content-type ", " application/x-www-form-urlencoded ");
XMLHttpRequest 1.0 API存在如下缺陷:
2014年11月W3C正式发布XMLHttpRequest Level 2(http://www.w3.org/TR/XMLHttpRequest2/)标准规范,新增了很多实用功能,推动异步交互在JavaScript中的应用。简单说明如下:
XMLHttpRequest 2为XMLHttpRequest对象新增timeout属性,使用该属性可以设置HTTP请求时限:
xhr.timeout = 3000;
上面语句将异步请求的最长等待时间设为3000 ms。超过时限,就自动停止HTTP请求。与之配套的有一个timeout事件,用来指定回调函数:
xhr.ontimeout = function(event){
console.log('请求超时!');
}
XMLHttpRequest 2新增FormData对象,使用它可以处理表单数据。使用方法如下。
第1步,新建FormData对象:
var formData = new FormData();
第2步,为FormData对象添加表单项:
formData.append('user', '张三');
formData.append('pass', 123456);
第4步,FormData对象可以直接获取网页表单的值:
var form = document.getElementById('myform');
var formData = new FormData(form);
formData.append('grade', '2'); //添加一个表单项
xhr.open('POST', form.action);
xhr.send(formData);
新版XMLHttpRequest对象不仅可以发送文本信息,还可以上传文件。使用send()方法可以发送字符串、Document对象、表单数据、Blob对象、文件和ArrayBuffer对象。
【示例】设计一个“选择文件”的表单元素(input[type=“file”]),将它装入FormData对象:
var formData = new FormData();
for (var i = 0; i < files.length;i++) {
formData.append('files[]', files[i]);
}
发送FormData对象给服务器:
xhr.send(formData);
XMLHttpRequest 2版本允许向不同域名的服务器发出HTTP请求。使用跨域资源共享的前提是:浏览器必须支持这个功能,且服务器端必须同意这种跨域。如果能够满足这两个条件,则代码的写法与不跨域的请求完全一样。例如:
var xhr = createXHR();
var url = 'http://other.server/and/path/to/script'; //请求的跨域文件
xhr.open('GET', url, true);
xhr.onreadystatechange = function (){
if ( xhr.readyState == 4 && xhr.status == 200 ){
console.log(xhr.responseText);
}
}
xhr.send();
新版本的XMLHttpRequest对象新增了responseType和response属性。
XMLHttpRequest 1.0版本只能从服务器接收文本数据,新版则可以接收二进制数据。使用新增的responseType属性,可以从服务器接收二进制数据。
把responseType设为blob,表示服务器传回的是二进制对象:
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = 'blob';
在接收数据的时候,用浏览器自带的Blob对象即可:
var blob = new Blob([xhr.response], {type: 'image/png'});
注意:是读取xhr.response,而不是读取xhr.responseText。
将responseType设为arraybuffer,把二进制数据装在一个数组里:
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = "arraybuffer";
在接收数据的时候,需要遍历这个数组:
var arrayBuffer = xhr.response;
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteArray.byteLength; i++) {
//执行代码
}
}
新版本的XMLHttpRequest对象新增了一个progress事件,用来返回进度信息,它分成上传和下载两种情况。下载的progress事件属于XMLHttpRequest对象,上传的progress事件属于XMLHttpRequest.upload对象。
第1步,先定义progress事件的回调函数:
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
第2步,在回调函数里面,使用这个事件的一些属性:
function updateProgress(event) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
}
}
在上面的代码中,event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable不为真,则event.total等于0。
与progress事件相关的还有其他5个事件,可以分别指定回调函数: