第68节 Ajax-Web前端开发之JavaScript-零点程序员-王唯「建议收藏」

第68节 Ajax-Web前端开发之JavaScript-零点程序员-王唯「建议收藏」Ajax是异步的JavaScript和XML,它不是一种新技术,是现有技术的集合,包括HTML、CSS、JavaScript、DOM、XML和X

大家好,欢迎来到IT知识分享网。

传统的Web技术:

传统的Web浏览器与服务器间采用同步交互的技术,用户表单完整的发送到服务器端进行处理后再返回一个新页面到浏览器。这一过程中因为网速延迟而导致浏览器有一定的等待时间,交互体验比较差;

Ajax的由来:
2005,Jessc James Garrett(杰斯克·詹姆斯·加勒特)提出了Ajax(Asynchronous Javascript + XML)(异步JavaScript和XML),是一种创建交互式网页应用的开发技术,其可以实现向服务器请求额外的数据而无须加载页面,也就是可以实现网页局部刷新;

Ajax是异步的JavaScript和XML,它不是一种新技术,是现有技术的集合,包括HTML、CSS、JavaScript、DOM、XML和XMLHttpRequest(简称为XHR)对象,其核心就是XMLHttpRequest对象,使用该对象可以与服务器通信,在不重载页面的情况下以异步的方式进行数据传输,也不是所谓的局部刷新;
Ajax中的X代表XML,但Ajax通信与数据格式无关,即从服务器取得的数据,不一定是XML数据;由于JSON的优势更大,所以在实际应用中,JSON和XML都会在Ajax中被应用;
XMLHttpRequest对象,最早是由微软引入的,其他浏览器后续都提供了相同的实现,并且后来W3C又提出了2级XMLHttpRequest规范(可以称为XHR2),但存在兼容性的问题。

实现Ajax的步骤:

  • 创建XMLHttpRequest对象;
  • 创建一个HTTP请求;
  • 设置响应HTTP请求事件处理函数;
  • 发送HTTP请求;
  • 等待请示的响应;
  • 使用DOM实现局部刷新;

XMLHttpRequest对象:

XMLHttpRequest对象是浏览器中实现通过HTTP协议和服务器交换DOM数据的程序集合

var xhr = new XMLHttpRequest();
console.log(xhr); // XMLHttpRequest

IT知识分享网

XMLHttpRequest继承自XMLHttpRequestEventTarget,而XMLHttpRequestEventTarget继承自EventTarget;XMLHttpRequestEventTarget接口中定义了7个相关事件,供XMLHttpRequest继承使用;

一个XMLHttpRequest对象代表一个独立的HTTP请求/响应对,所以,如果重用这个对象的话,就会终止之前通过该对象挂起的任何请求;

在IE5.0中,XHR通过MSXML库中ActiveXObject来实现的,如:

IT知识分享网var xhr = new ActiveXObject("Microsoft.XMLHTTP");
console.log(xhr); // [object] { }

在IE中可能遇到3种不同版本的XHR对象,即:MSXML2.XMLHttp、MSXML2.XMLHttp.3.0和MSXML2.XMLHttp.6.0,如:

function createXHR(){
// arguments 就是createXHR()函数的参数集合
// callee 指向的就是createXHR()
// arguments.callee.activeXString就是判断createXHR有没有activeXString这个自定义属性
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
for(var i=0, len=versions.length; i < len; i++){
try{
var xhr = new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
return xhr;
}catch(ex){
throw new Error("浏览器不支持MSXML");
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
var xhr = createXHR();
console.log(xhr); // [object] { }

IE7及以后和标准浏览器支持原生的XHR对象,兼容方案:

IT知识分享网function createXHR(){
if (typeof XMLHttpRequest != "undefined") {
return new XMLHttpRequest();
}else if (typeof ActiveXObject != "undefined") {
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
for(var i=0, len=versions.length; i < len; i++){
try{
var xhr = new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
console.log("xhr被创建了");
return xhr;
}catch(ex){
throw new Error("浏览器不支持MSXML");
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}else{
throw new Error("不支持XHR对象");
}
}

IE模拟XMLHttpRequest对象,如:

if (window.XMLHttpRequest === undefined) {
window.XMLHttpRequest = function(){
try {
// 如果可用,则使用ActiveX对象的最新版本
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
} catch (error) {
try{
// 否则,回退到较旧的版本
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
}catch(e){
throw new Error("不支持XMLHttpRequest")
}
}
}
}

发送请求:
XMLHttpRequest对象创建后,可用open()方法来指定要发送的请求;
语法: xhr.open(method, url [,async][,user][, password]);参数method为要发送的请求的类型,可能的值为GET、POST、HEAD或者其他受服务器支持的HTTP方法;参数url为请求的URL地址;可选的async表示请求是否以异步方式发送请求的布尔值,默认为true,即开启异步;user和password用于用户名和密码的认证,一般不使用;如

xhr.open("get","example.php",false);

请求的URL可以使用相对或绝对路径,但只能向同一个域中使用相同端口和协议的URL发送请求;
打开请求后,并不会真正发送请求,而只是启动一个请求以备发送,还要使用send()方式将其发送出去;
语法: xhr.send([arg]);可选参数arg表示作为请求主体发送的数据,如果不需要通过请求主体发送数据,则传入null或省略;如:

xhr.send(null);

调用send()方法后,请求就会被发送到服务器;
GET请求是没有主体的,所以应该传入null,而POST请求通常拥有主体,例如发送表单数据,此时应该传入所要提将把数据,并应该使用setRequestHeader()方法设置相匹配的“Content-Type”头;

响应请求:
此后,浏览器会等待请求返回;返回响应后,响应数据会自动填充XMLHttpRequest对象的属性,相关的属性如下:

  • responseText:作为响应主体被返回的文本;
  • responseXML:如果响应的内容类型是“text/xml”或”application/xml”,该属性中保存着响应数据的XML DOM文档,否则为null;
  • status:以数字形式返回响应的HTTP状态码;
  • statusText:以文本形式返回响应的HTTP状态说明;

在接收到响应后,第一步是检查status属性,以确定响应是否已经成功返回,此属性中会放入请求的HTTP状态码,其使用标准的HTTP值,如200表示正常,404表示没有找到,同时,其statusText属性中放入描述信息,如“OK”对应status的200,表示请求成功,“Not Found”对应status的404;

如果成功响应,其responseText属性或responseXML属性就可以访问了,也就是从服务器返回的数据;如:

console.log(xhr.status);
console.log(xhr.statusText);
console.log(xhr.responseText);
console.log(xhr.responseXML);

status状态码如果是304,代表请示的资源并没有被修改,可以直接使用浏览器中缓存的数据;为了确保可以接收到适当的响应,应该检查状态码:

if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
    console.log(xhr.status);
    console.log(xhr.statusText);
    console.log(xhr.responseText);
    console.log(xhr.responseXML);    
}else{
    console.log("未获取到数据,status:" + xhr.status);
}

读取XML,如:

xhr.open("get","example.xml",false);
xhr.send(null);
if(xhr.status == 200){
var result = xhr.responseXML;
console.log(result);
console.log(result.getElementsByTagName("name")[0].textContent);
}

readyState属性:

如果要发送异步请求, 可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段,值如下:

  • UNSENT 0:未初始化,尚未调用open()方法;
  • OPENED 1:启动;已经调用open()方法,但尚未调用send()方法;
  • HEADERS_RECEIVED 2:发送;已经调用send()方法,但尚未接收到响应;
  • LOADING 3:交互、接收;已经接收到部分响应数据;
  • DONE 4:完成;已经接收全部响应数据,而且已经可以在客户端使用了;

只要readyState属性值发生变化,都会触发一次readystatechange事件;

xhr.onreadystatechange = function(){
console.log(xhr.readyState); // 2 3 4
}

实际上,当readyState值为0或1时,并没有触发该事件;通常,只关注readyState为4的阶段,因为此时所有数据都已经准备就绪;
需要注意的是:必须在调用open()之前指定onreadstatechange事件处理程序才能确保跨浏览器兼容性;

var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
console.log(xhr.responseText);
}else{
console.log("当前请求状态:" + xhr.status);
}
}
};
xhr.open("get","example.txt",true);
xhr.send(null);

示例:

<button id="ajaxBtn" type="button">发送一个请求</button>
<script>
(function() {
var xhr;
document.getElementById("ajaxBtn").addEventListener('click', makeRequest);
function makeRequest() {
xhr = new XMLHttpRequest();
if (!xhr) {
alert('不支持XHR');
return false;
}
xhr.onreadystatechange = alertContents;
xhr.open('GET', 'example.txt');
xhr.send();
}
function alertContents() {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
alert(xhr.responseText);
} else {
alert('请求不成功');
}
}
}
})();
</script>

示例:获取XML;

document.getElementById("ajaxBtn").addEventListener('click', function(){
var xhr = new XMLHttpRequest();
xhr.open("GET","example.xml");
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
var xmldoc = xhr.responseXML;
var root_node = xmldoc.getElementsByTagName('root').item(0);
console.log(xmldoc);
console.log(root_node);
}
};
xhr.send();
});

示例:获取文本形式的请求主体:

var xhr = createXHR();
xhr.open("GET","message.php?id=2",true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4 && xhr.status == 200) {
var type = xhr.getResponseHeader("Content-Type");
console.log(type);
if (type.match(/^text/)) { // 确保响应的是文本
console.log(xhr.responseText);
}
}
}
xhr.send(null);
message.php:
<?php
if(isset($_GET["id"])){
echo "ID:2的记录信息";
}

abort()方法:
在接收到响应之前(即readyState变成4之前)还可以调用abort()方法取消请求的操作;调用这个方法后,XHR对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性,XHR对象的readyState和status属性会被重置为0;

xhr.onreadystatechange = function(){
if(xhr.readyState == 3){
xhr.abort();
}else if(xhr.readyState == 4){
console.log(xhr.responseText);
}
}

在终止请求或使用后,要对XHR对象进行解引用操作;由于内存原因,不建议重用XHR对象;

使用HTTP头部:

每个HTTP请求和响应都包含一组带有额外信息的头部(即请求头部和响应头部),供开发人员使用;
默认情况下,发送XHR请求的同时,还会发送以下头部信息:

  • Accept:浏览器能够处理的内容类型;
  • Accept-Charset:浏览器能够显示的字符集;
  • Accept-Encoding:浏览器能够处理的压缩编码;
  • Accept-Language:浏览器当前设置的语言;
  • Connection:浏览器与服务器之间的连接类型;
  • Cookie:当前页面设置的任何Cookie;
  • Host:发出请求的页面所在的域;
  • Referer:发出请求的页面的URI;
  • User-Agent:浏览器的用户代理字符串;

不同浏览器实际发送的头部信息会有所不同;

XMLHttpRequest对象提供了获取和设置这两种头部信息的方法;
使用setRequestHeader()方法可以设置自定义的请求头部信息;语法:setRequestHeader(name, value);参数name和value分别为头部字段的名称和头部字段的值;

需要注意的是,要成功发送请求头部信息,必须在调用open()方法之后且在调用send() 方法之前调用setRequestHeader()方法;

xhr.open("get","example.txt",true);
xhr.setRequestHeader("MyHeader","MyValue");
xhr.send(null);

服务器在接收到自定义的头部信息后,可以执行相应的后续操作;

有的浏览器允许开发人员重写默认的头部信息,但有的不允许;如在POST请求需要Content-Type头指定请求主体的MIME类型:

xhr.setRequestHeader("Content-Type","text/plain");

或使用Ajax提交表单时设置编码类型:

xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

有些头信息是不允许重写的,例如不能指定“Content-Length”、“Date”、“Referer”或“User-Agent”等头信息,这些头信息是XMLHttpRequest自动添加的,以防止被伪造;以下头信息不能重写:Accept-Charset、Accept-Encoding、Connection、Content-Length、Cookie、Cookie2、Content-Transfer-Encoding、Date、Expect、Host、Keep-Alive、Referer、TE、Trailer、Transfer-Encoding、Upgrade、User-Agent、Via;

如果调用setRequestHeader()方法多次对相同的头进行设置,新值不会取代前面设置的值,相反,会把相同的多个头全部进行发送;

顺序问题:
HTTP请求的各部分是有顺序的:请求方法和URL首先到达(服务器),然后是请求头,最后是请求主体;其对应的XMLHttpRequest对象请求的顺序,即先调用open(),再调用setRequestHeader()方法设置头,最后调用send()方法;

示例:使用POST发送文本字符串,如:

var xhr = createXHR();
xhr.open("POST","log.php",true);
xhr.onload = function(){
console.log(xhr.responseText);
}
// 用请求主体发送纯文本消息
xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
var data = "零点程序员 2022-12-12";
xhr.send(data);

log.php:

<?php
$data = file_get_contents('php://input');
echo "接收到的纯文本:" . $data;

获取头部信息:

getAllResponseHeaders():可以返回包含所有的HTTP头部信息的长字符串,其中以\r\n分隔;getResponseHeader(“name”):获取指定的头部;

xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
var allHeaders = xhr.getAllResponseHeaders();
console.log(allHeaders); // 换行的头信息
var arr = allHeaders.trim().split(/[\r\n]+/);
var headerMap = {};
arr.forEach(function(line){
var parts = line.split(': ');
var header = parts.shift();
var value = parts.join(': '); // : 没有意义,纯粹是把只有一个元素的数组转换为字符串
headerMap[header] = value;
});
// 随便读取一个值
var contentType = headerMap["content-type"];
console.log(contentType);
console.log(xhr.getResponseHeader("Content-Type"));
}
};

示例:期望获取某种类型的数据;

// var my_expected_type = "text/plain";
var my_expected_type = "application/json";
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 2 && xhr.status == 200){
var contentType = xhr.getResponseHeader("Content-Type");
if(contentType != my_expected_type)
xhr.abort();
}else if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
}
};
xhr.onabort = function(){
console.log("请求中止");
};
// xhr.open("GET", "example.txt");
xhr.open("GET", "example.json");
xhr.send();

在服务器端,也可以利用头部信息向浏览器发送额外的、结构化的数据;

示例:一个小型XMLHttpRequest库;

XHConn.js:
function XHConn(){
var xmlhttp, bComplete = false;
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}catch (e) {
try {
xmlhttp = new XMLHttpRequest();
}catch (e) {
xmlhttp = false;
}
}
}
if (!xmlhttp) return null;
this.connect = function(sURL, sMethod, sVars, fnDone){
if (!xmlhttp) return false;
bComplete = false;
sMethod = sMethod.toUpperCase();
try {
if (sMethod == "GET"){
xmlhttp.open(sMethod, sURL+"?"+sVars, true);
sVars = "";
}else{
xmlhttp.open(sMethod, sURL, true);
// xmlhttp.setRequestHeader("Method", "POST "+sURL+" HTTP/1.1");
xmlhttp.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
}
xmlhttp.onreadystatechange = function(){
if (xmlhttp.readyState == 4 && !bComplete){
bComplete = true;
fnDone(xmlhttp);
}
};
xmlhttp.send(sVars);
}catch(z) {
return false;
}
return true;
};
return this;
}

应用:

<script src="scripts/XHConn.js"></script>
<script>
var myConn = new XHConn();
if(!myConn)
console.log("不支持XMLHttpRequest");
var fnWhenDone = function(response){
console.log(response.responseText);
};
myConn.connect("example.php","POST", "username=wangwei&age=18", fnWhenDone);
</script>

example.php:

<?php
$username = $_POST['username'];
$age = $_POST['age'];
echo $username.' age is:'.$age;

响应解码:

服务器可以返回多种MIME类型的数据,如“text/plain”、“text/html”、“text/css”等类型,此时可以使用responseText属性获取这些数据;但如果服务器响应的是XML或XML文档,此时,就可以使用responseXML属性获得一个已经解析的XML文档,即该属性值是一个Document对象,直接可以通过相关的DOM方法处理它;另外,它的值不仅可以是XML文档,也可以是HTML Document,浏览器并没有实现;

如果服务器响应的是诸如对象或数组这样的结构化数据,它应该序列化该对象并传输其JSON字符串;此时,可以使用responseText属性获取,并可传递给JSON.parse()方法解析为JavaScript对象,如:

var xhr = createXHR();
var url = "example.json"
xhr.open("GET",url,true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4 && xhr.status == 200) {
var type = xhr.getResponseHeader("Content-Type");
console.log(type); // application/json
// 检查类型,确保是xml或json类型
if (type.indexOf("xml") !== -1 && xhr.responseXML) {
console.log(xhr.responseXML); // Document对象响应
}else if (type == "application/json") {
console.log(JSON.parse(xhr.responseText)); // JSON响应
}
}
}
xhr.send(null);

请求类型:

GET请求:
GET请求是常用的请求类型,目的就是向服务器查询某些信息,因此,在其URL的末尾会追加它的查询字符串,具体格式是URL的结尾添加问号,其后跟着用&号连接起来的名称/值,并且名称和值应该进行编码,如:http://www.zeronetwork.cn/page?name1=value1&name2=value2&name3=value;要用XMLHttpRequest对象发送一个GET请求,只需将URL(带有参数)传入open()方法即可,同时第一个参数为GET,如:

xhr.open("GET", "http://www.zeronetwork.cn/page?name1=value1", false);

使用GET请求最常见的错误就是查询字符串的格式有问题,查询字符串的每个参数的名称和值都必须使用encodeURIComponent()([kəmˈpəʊnənt]组成部)进行编码,然后才能放到URL的末尾;

封装成一个函数:

function addURLParam(url, name, value){
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
var url = "example.php";
url = addURLParam(url, "name", "王 唯");
url = addURLParam(url, "sex", true);
url = addURLParam(url, "age", 18);
xhr.open("get",url,false);
xhr.send(null);

进行POST请求:

POST请求一般用于向服务器发送需要保存的数据,特别是提交表单时;如:

xhr.open("post", "example.php", false);

表单中的数据(每个表单控件的name和值)会编码到一个字符串中并随请求发送,即对每个表单元素的名字和值执行普通的URL编码(使用16进制转义码替换特殊字符),并形成键值对,再使用&分隔这些键值对,即与GET方式相同;并把其传入send()方法中,如:

xhr.send("name=王唯&sex=true&age=18");

example.php:

<?php
$name = $_POST["name"];
$sex = $_POST["sex"];
$age = $_POST["age"];
echo $name.",".$sex.",".$age;

POST请求常用于HTML表单,它在请求主体中包含额外数据,因此,在XMLHttpRequest进行POST请求时,必须设置Content-Type头,值为application/x-www-form-urlencoded,即表示表单提交时的编码类型,也就相当于表单中的enctype属性,如:

xhr.open("post", "example.php", true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send("name=王 唯&sex=true&age=18");

可以把请求主体数据封装一下函数,如:

function addPostParam(params, name, value){
if(params.length > 0){
params += "&";
}
return params + encodeURIComponent(name) + "=" + encodeURIComponent(value);
}
var data = "";
data = addPostParam(data, "name","王唯");
data = addPostParam(data, "sex", true);
data = addPostParam(data, "age", 18);
xhr.open("post", "example.php", false);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send(data);

如果表单控件有type=file的话,就需要设置Content-Type为multipart/form-data,此时,数据会被编码为一条消息,表单中的每个控件对应消息中的一个部分;也就是把表单所有的数据封装到HTTP body中,再一起发送给服务器;
此时,服务器端就需要使用另外一种方式来获取提交的数据,如:

$data = file_get_contents('php://input');
echo $data;

利用Ajax提交表单:

var btn = document.getElementById("btn");
btn.addEventListener("click", function(event){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
console.log(xhr.responseText);
document.body.appendChild(document.createTextNode(xhr.responseText));
}else{
console.log("当前请求状态:" + xhr.status);
}
}
};
xhr.open("post", "example.php", true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
var name = document.forms[0].elements["name"].value;
var sex = document.forms[0].elements["sex"].value;
var age = document.forms[0].elements["age"].value;
var data = "name="+name+"&sex="+sex+"&age="+age;
xhr.send(data);
});

还可以对页面中的表单的数据进行序列化,再通过XHR发送到服务器,如:

xhr.open("post","example.asp",true);
xhr.setRequestHeader("Content-type","application/w-www-form-urlencoded");
var form = document.getElementById("myform");
xhr.send(serialize(form));

示例:序列化对象并发送:

function encodeFormData(data){
if (!data) return "";
var pairs = []; // 保存键值对
for(var name in data){
if (!data.hasOwnProperty(name)) continue; // 跳过非实例属性
if(typeof data[name] === "function") continue; // 跳过方法
var value = data[name].toString(); // 把值转换成字符串
name = encodeURIComponent(name.replace("%20", "+")); // 把空格转+号
value = encodeURIComponent(value.replace("%20", "+"));
pairs.push(name + "=" + value);
}
return pairs.join("&");
}
function postData(url, data, callback){
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200 && callback){
callback(xhr); // 调用回调函数
}
};
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(encodeFormData(data));
}
var person = {
name: "王唯",
sex: true,
age: 18,
smoking: function(){}
};
postData("example.php", person, function(xhr){
console.log(xhr.responseText);
});

在Ajax中使用JSON:

示例:使用GET请求,获取JSON字符串

[
{
"name": "Wangwei","email": "wangwei@zeronetwork.cn"
},
{
"name": "JingJing","email": "jingjing@zeronetwork.cn"
},
{
"name": "Juanzi","email": "juanzi@zeronetwork.cn"
}
]

example.php为:

$jsonText = '[
{
"name": "Wangwei","email": "wangwei@zeronetwork.cn"
},
{
"name": "JingJing","email": "jingjing@zeronetwork.cn"
},
{
"name": "Juanzi","email": "juanzi@zeronetwork.cn"
}
]';
echo $jsonText;
)

JavaScript:

var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
var contacts = JSON.parse(xhr.responseText);
var list = document.createElement("ul");
list.id = "contacts";
for(var i=0,len=contacts.length; i<len; i++){
var li = document.createElement("li");
li.innerHTML = "<a href=\"mailto:>" + contacts[i].email + "\">" + contacts[i].name + "</a>";
list.appendChild(li);
}
document.body.appendChild(list);
}
}
};
xhr.open("get","example.php",true);
xhr.send(null);

示例:使用POST请求,并获取响应的JSON字符串,如:

<input type="text" id="username" />
<input type="button" id="btn" value="提交表单" />
<script>
var xhr = null;
document.getElementById("btn").onclick = function(){
var username = document.getElementById("username").value;
makeRequest('example.php', username);
};
function makeRequest(url, username){
xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = xhrHandler;
xhr.send('username=' + encodeURIComponent(username));
}
// 例如服务器处理完数据后,返回的数据格式JSON字符串为
// {"userName":"王唯","userString":"大师哥王唯"}
function xhrHandler(){
if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200){
var data = JSON.parse(xhr.responseText);
console.log(data.userstring);
}
}
</script>

example.php为:

<?php
$username = (isset($_POST['username'])) ? $_POST['username'] : 'no name';
$userstring = "大师哥:" . $username;
$array = ['username' => $username, 'userstring' => $userstring];
echo json_encode($array);

也可以把JSON数据发送给服务器,一般要把JSON放到POST请求主体中,即把JSON文本传递给send()方法,如:

var xhr = createXHR();
var contact = {
name: "王唯",
email: "wangwei@zeronetwork.cn"
};
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
}
};
xhr.open("post","example.php",true);
// xhr.setRequestHeader("Content-Type", "application/json"); // 可以不需要
xhr.send(JSON.stringify(contact));

发送XML文档:

一个XML Document对象也可以作为主体被发送;例如一个XML文档:

<root>
<person sex="男" age="18">
王唯
</person>
</root>

前端:

function postQuery(url, name, sex, age, callback){
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && callback)
callback(xhr);
};
// 创建XML Document
var doc = document.implementation.createDocument("", "root",null);
var root = doc.documentElement;
var person = doc.createElement("person");
root.appendChild(person);
person.setAttribute("sex", sex);
person.setAttribute("age", age);
person.appendChild(doc.createTextNode(name));
// 发送XML,会自动设置Content-Type头
xhr.send(doc);
}
postQuery("xml.php", "王唯", "男", 18, function(xhr){
console.log(xhr.responseText);
console.log(xhr.responseXML);
});

xml.php:

<?php
header('Content-Type:text/xml;charset=utf-8');
$xmldata = file_get_contents("php://input");
echo $xmldata;

GET与POST请求的区别在于:

  • GET主要是从服务器获取数据,而POST则是向服务器传送数据;
  • POST请求把数据作为请求的主体提交,而GET请求不是这样;
  • POST请求的主体可以包含非常多的数据,发送的数据量大,而且格式不限;但GET发送的数据量是有限制的
  • GET安全性非常低,POST安全性较高;
  • 从性能上来看,POST请求消耗的资源会更多一些,所以GET的速度比POST的速度更快;

除了GET和POST之外,XMLHttpRequest也允许 “DELETE”、“HEAD”、“OPTIONS”和“PUT”等请求类型,但老旧浏览器并不支持所有这些方法,但“HEAD”却得到广泛支持;

HEAD请求:
向服务器请求与GET请求相一致的响应,只不过响应体中没有具体的内容,只用于获取报头;作用在于读取服务器的响应头部而忽略其内容,通过读取响应头部可以获取Content-Type、LastModified等头部信息,从而可以用来验证服务器是否正常运行或者获取一些服务器的信息;由于发送HEAD请求时,服务器只返回响应头部而忽略内容,因此HEAD请求的响应比GET或者POST都要快得多;在得到响应后,再使用XHR对象的getAllResponseHeaders()和getLastModified,getIsResourceAvailable()方法读取响应头部,修改时间和资源是否可用等信息;如:

<script type="text/javascript">
var xmlHttp = new XMLHttpRequest();
var requestType="";
function headerRequest(type,url) {
requestType = type;
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("Head",url,true);
xmlHttp.send(null);
}
function handleStateChange() {
if(xmlHttp.readyState == 4) {
if(requestType == "allResponseHeaders") {
getAllResponseHeaders();
}else if(requestType == "lastModified") {
getLastModified();
}else if(requestType == "isResourceAvailable") {
getIsResourceAvailable();
}
}
}
function getAllResponseHeaders() {
alert(xmlHttp.getAllResponseHeaders());
}
function getLastModified() {
alert("Last Modified: " + xmlHttp.getResponseHeader("Last-Modified"));
}
function getIsResourceAvailable() {
if(xmlHttp.status == 200) {
alert("Successful response");
}
}
</script>
<h2>读取网页响应头</h2>
<p><button onclick="headerRequest('allResponseHeaders','demo.xml')">读取所有文件头</button>
<button onclick="headerRequest('lastModified','demo.xml')">获取文件修改日期</button>
<button onclick="headerRequest('isResourceAvailable','demo.xml')">读取现存资源</button></p>

URLSearchParams类:

定义了一些实用的方法来处理URL的查询字符串,IE不支持;
构造函数:URLSearchParams([init]),创建并返回一个新的URLSearchParams 对象;init参数为一个可选的查询字符串;如:

var searchParams = new URLSearchParams('?name=wangwei&age=18');
console.log(searchParams); // URLSearchParams
console.log(searchParams.toString()); // name=wangwei&age=18

开头的’?’ 字符会被忽略;

URLSearchParams对象方法:

append(name, value):
插入一个指定的name键和value值对作为新的搜索参数;如:

var searchParams = new URLSearchParams();
var searchParams = new URLSearchParams('?name=wangwei');
searchParams.append("age", 18)
console.log(searchParams.toString()); // name=wangwei&age=18

delete(name)方法:可以删除指定名称的所有搜索参数;

searchParams.delete("name");
console.log(searchParams.toString()); // age=18

get(name)和getAll(name)方法:
get()方法获取指定搜索参数的第一个值,如果要获取指定搜索参数的所有值,可以使用getAll()方法,其返回是一个数组,如果该键不存在返回null或空数组;如:

searchParams.append("name", "JingJing");
var name = searchParams.get("name");
var age = parseInt(searchParams.get("age"), 10);
console.log(name, age);
console.log(searchParams.getAll("name")); // ['wangwei', 'JingJing']

has(name):
判断是否存在此搜索参数, 返回 Boolean,如:

console.log(searchParams.has("name")); // true

entries()方法:
返回一个iterator,包含所有键值对的对象;如:

for(var pair of searchParams.entries()) {
console.log(pair[0]+ '='+ pair[1]);
}

keys()方法:
返回iterator,包含键值对的所有键名;如:

for(var key of searchParams.keys()) {
console.log(key);
}

values()方法:
返回iterator,包含键值对的所有值;如:

for(var value of searchParams.values()) {
console.log(value);
}

set(name, value)方法:
设置一个搜索参数的新值,假如原来有多个值将删除其他所有的值,如果将要设置的值不存在,则创建它;

searchParams.set("name", "大师哥王唯");

sort()方法:
按键名排序,如:

var searchParams = new URLSearchParams("c=4&a=2&b=3&a=1");
searchParams.sort();
console.log(searchParams.toString()); // a=2&a=1&b=3&c=4

toString()方法:
返回搜索参数组成的字符串,可直接使用在URL上;

URLSearchParams构造函数不会解析完整 URL;如:

var searchParams = new URLSearchParams('https://www.zeronetwork.cn?name=wangwei&age=18');
console.log(searchParams.toString());
for(var pair of searchParams.entries()) {
console.log(pair[0]+ '='+ pair[1]);
}

init参数也可以是二维数组或一个对象,如:

var searchParams = new URLSearchParams([["name", "wangwei"],["age", 18]]);
var searchParams = new URLSearchParams({name:"wangwei", age:18}); // 或者
var searchParams = new URLSearchParams({"name":"wangwei", "age":18});
console.log(searchParams.toString()); // name=wangwei&age=18

在Ajax中使用URLSearchParams对象,如:

var searchParams = new URLSearchParams([["name", "wangwei"],["age", 18]]);
searchParams.append("sex", true);
searchParams.append("friends[]", "JingJing");
searchParams.append("friends[]", "Juanzi");
var xhr = createXHR();
xhr.open("post","example.php",true);
xhr.onreadystatechange = function(event){
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
}
xhr.send(searchParams);

URLSearchParams兼容性处理:

可以使用第三方库url-search-params-polyfill,地址:https://github.com/jerrybendy/url-search-params-polyfill;如:

<script src="scripts/index.js"></script>
<script>
var search = new URLSearchParams();
var search = new URLSearchParams("id=1&from=home");
var search = new URLSearchParams({id: 1, from:"about"});
var search = new URLSearchParams(window.location.search);
var search = new URLSearchParams(search);
var search = new URLSearchParams([["name", "wangwei"], ["age", 18]]);
console.log(search.toString());
search.append("sex","男");
console.log(search.toString());
</script>

XMLHttpRequest2级:

XMLHttpRequest1级只是把已有的XHR对象的实现细节描述了出来,而XMLHttpRequest2级则进一步发展了XHR,但并不是所有浏览器都完整的实现了XMLHttpRequest2级,只是实现了它规定的部分;

FormData:

现代Web应用中经常使用的一项功能就是表单数据的序列化,XMLHttpRequest2级为此定义了FormData类型;FormData为序列化表单以及创建与表单格式相同的数据提供了便利,如

var data = new FormData();
data.append("name","wangwei");
console.log(data); // FormData

IE9及以下浏览器不支持;

FormData提供了一种表示表单数据的键值对key/value的构造方式,可以方便的将数据通过XMLHttpRequest.send() 方法发送出去;如:

var data = new FormData();
data.append("name","wangwei");
var xhr = createXHR();
xhr.open("post","example.php",true);
xhr.onreadystatechange = function(event){
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
}
xhr.send(data);

使用FormData的方便之处就是不必明确地在XHR对象上设置请求的头部;XHR对象能够识别传入的数据类型是FormData的实例,并配置适当的头部信息

FormData方法:
提供了若干个方法,专门处理它的键和值;

append(name, value, [filename])方法:
向 FormData 中添加新的属性值,键name和value值,分别对应表单字段的名称和值;

var data = new FormData();
data.append("name","wangwei");
data.append("sex",true);

可以添加任意多的键值对,如果添加的键已存在,会覆盖原值,如果键不存在则新增一项属性值;

但如果存在的键的名称带有[](类似于数组,或者像一组表单控件的name值),会把新值添加到已有值的后面,如:

data.append("friends[]", "JingJing");
data.append("friends[]", "Juanzi");

第三个参数filename是可选的,表示传给服务器的文件名称,但此时,第二个参数value应该是一个File对象;如:

// data.append("photo", document.forms[0].elements["photo"].files[0], "images/1.jpg");
var file = new File(["Web前端开发"], "web.txt", {type: "text/plain",});
data.append("photo", file, "我的文件");

如果要添加多个上传文件的话,可以:

data.append('pic[]', myFile.files[0], 'img1.jpg');
data.append('pic[]', myFile.files[1], 'img2.jpg');

另外,还可以添加Blob对象,如:

var content = '<a id="a"><b>zeronetwork</b></a>';
var blob = new Blob([content], {type: "text/xml"});
data.append("blob", blob);

delete(name)方法:
使用指定的键name从FormData对象中键和值,IE不支持;如:

data.delete("name");

get(name)和getAll(name)方法:
get()方法用于返回FormData对象中和指定的name键关联的第一个值,如果想要返回和指定键关联的全部值,需要使用getAll()方法,该方法返回包含所有的值的数组,IE不支持;如:

data.append("name","wangwei");
data.append("name","JingJing");
console.log(data.get('name')); // wangwei
console.log(data.getAll('name')); // ['wangwei', 'JingJing']

has(name)方法:
返回一个布尔值,表示该FormData对象是否含有某个key,IE不支持,如:

console.log(data.has("name")); // true
if (data.has("age")) {
data.append("age", 18);
}

entries()方法:
返回一个iterato对象,此对象可以遍历访问FormData中的键值对,IE不支持;如:

for(var param of data.entries()) {
console.log(param[0]+ ' : '+ param[1]);
}

keys()方法:
返回一个迭代器(iterator),遍历了该 formData包含的所有key,IE不支持;如:

for (var key of data.keys()) {
console.log(key);
}

values()方法:
返回一个允许遍历该对象中所有值的迭代器,IE不支持,如:

for (var value of data.values()) {
console.log(value);
}

set(name, value,[ filename])方法:
会对FormData对象里的某个key设置一个新的值,如果该key不存在则添加新的键和值;使用方式同append()方法,IE不支持,如:

data.set('name', '大师哥王唯');

set()和append()方法不同之处在于:如果某个key 已经存在,set()会直接覆盖所有该key对应的值,而append()则是在该key的最后位置再追加一个新值;

通过表单创建FormData对象:

FormData不仅可以发送键值对数据,也可以发送表单数据;通过向FormData构造函数中传入表单对象,就可以把表单控制的数据预先映射到相关的键值对中,如:

var data = new FormData(document.forms[0]);
console.log(data);

结构:

<form id="myform">
<p>名字:<input type="text" name="name" value="王唯" /></p>
<p>性别:<input type="radio" name="sex" value="1" checked />男
<input type="radio" name="sex" value="0" />女</p>
<p>年龄:<input type="text" name="age" value="18" /></p>
<p><input type="button" id="btn" value="Ajax提交">
<input type="submit" value="Submit提交" /></p>
</form>

JavaScript:

var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
console.log(xhr.responseText);
}
}
};
xhr.open("post", "example.php", true);
var form = document.getElementById("myform");
var data = new FormData(form);
// 表单控件的值已经保存到FormData中了
console.log(data.get("name"));
console.log(data.get("age"));
xhr.send(data);

example.php:

<?php
$name = $_POST["name"];
$sex = $_POST["sex"];
$age = $_POST["age"];
echo "姓名:$name, 性别:$sex, 年龄:$age";

FormData将仅使用具有name属性的表单控件

在创建一个包含Form表单数据的FormData对象之后和发送请求之前,可以添加额外的键值对数据到FormData对象里;如:

var serialnumber = 0;
var form = document.getElementById("myform");
var data = new FormData(form);
xhr.open("post", "example.php", true);
data.append("job", "程序员");
data.append("serialnumber", serialnumber++);
xhr.send(data);

使用FormData对象上传文件,如在表单中添加一个file控件:

<p><input type="file" name="file" /></p>

FormData默认情况下也包括该file上传文件,后端可以获取此上传文件,如:

$file = $_FILES["file"]["name"];
echo $file;

如果不传入表单的话,也可以把上传文件直接append到FormData中,如:

btn.addEventListener("click", function(event){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
}
};
var data = new FormData();
xhr.open("post", "example.php", true);
data.append("name", document.forms[0].elements["name"].value);
data.append("sex", document.forms[0].elements["sex"].value);
data.append("age", document.forms[0].elements["age"].value);
var file = document.forms[0].elements["file"].files[0];
if (file) {
data.append("file", file, "我的文件");
}
xhr.send(data);
});

responseURL属性:

返回响应的序列化URL,如果URL为空则返回空字符串;如果URL有锚点,则位于#后面的内容会被删除;如果URL有重定向,responseURL的值会是经过多次重定向后的最终URL,如:

xhr.open("get","example.php",true);
xhr.onload = function(){
// http://localhost/example.php
console.log(xhr.responseURL);
}
xhr.send(null);

IE不支持此属性;

responseType属性:

一个用于定义响应类型的枚举值(enumerated value);如果将responseType的值设置为空字符串,则会使用text作为默认值;

console.log(xhr.responseType); // 空

可能的值:

  • “”:空的responseType字符串与默认类型 “text” 相同;
  • “arraybuffer”:响应是一个包含二进制数据的ArrayBuffer;
  • “blob”:响应是一个包含二进制数据的Blob对象;
  • “document”:响应是一个HTML Document或XML XMLDocument;
  • “json”:将响应的数据内容解析为JSON对象,可直接使用;
  • “text”:响应的数据类型是文本;
  • “ms-stream”:响应是流式下载的一部分;此响应类型仅允许用于下载请求;

该属性要在调用open()初始化请求之后调用,并且要在调用send()发送请求到服务器之前调用;

var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
// console.log(xhr.responseText);
// console.log(xhr.responseXML);
console.log(xhr.response);
// var person = xhr.response;
// console.log(person.name + "," + person.age);
}
};
// xhr.open("get","example.php",true);
// xhr.responseType = "text";
// xhr.open("get","example.xml",true);
// xhr.responseType = "document";
// xhr.open("get","example.json",true);
// xhr.responseType = "json";
// xhr.open("get","example.txt",true);
// xhr.open("get","images/1.jpg",true);
// xhr.responseType = "blob";
// xhr.open("get","images/1.jpg",true);
// xhr.open("get","example.txt",true);
// xhr.responseType = "ms-stream";
// xhr.open("get","images/1.jpg",true);
xhr.open("get","example.txt",true);
xhr.responseType = "arraybuffer";
console.log(xhr.responseType);
xhr.send(null);

将responseType设置为某个值时,应确保服务器实际发送(返回)的响应与该格式兼容,否则,则response的值将为null;

如果在同步模式下修改responseType属性,会抛出InvalidAccessError异常;

response属性:

返回响应的正文,其类型为ArrayBuffer、Blob、Document、JavaScript Object或DOMString中的一个,具体是哪种类型取决于responseType属性的值;

XMLHttpRequest中的HTML:

XMLHttpRequest2规范添加了对HTML语法解析功能, 此前仅支持XML语法解析,该功能允许HTML作为解析的DOM,但HTML必须在异步的情况下才可以;并且,只有当responseType属性设置为’document’的情况下,才支持HTML;

如:example.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Web前端开发</title>
</head>
<body>
<h1>零点程序员</h1>
<p>大师哥王唯</p>
</body>
</html>
JavaScript:
var xhr = new XMLHttpRequest();
xhr.onload = function(){
// console.log(this.responseText); // 抛出异常
console.log(this.responseXML); // #document
console.log(this.response); // #document
console.log(this.response.title); // Web前端开发
};
xhr.open("GET", "example.html");
xhr.responseType = "document";
xhr.send();

有些旧浏览器并不支持responseType,如果使用的话,会抛出异常,所以最好使用try/catch语句,另外,IE9以下也不支持response属性,所以最好使用responseXML属性,如:

try{
xhr.responseType = "document";
}catch(e){
console.log("不支持responseType属性");
}

overrideMimeType(mimeType)方法:

Firefox最早引入了overrideMimeType()方法,用于重写XHR响应的MIME类型;这个方法后来也被纳入了XMLHttpRequest2级规范;因为返回响应的MIME类型决定了XHR对象如何处理它,所以提供一种方法能够重写服务器返回的MIME类型是很有用的;参数mimeType指定了一个具体的MIME类型;
调用overrideMimeType()方法必须在send()方法之前,才能保证重写响应的MIME类型;如:example.php:

$str = "<root><person><name>王唯</name><age>18</age></person></root>";
echo $str;
JavaScript:
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if (xhr.status == 200) {
// console.log(xhr.responseText);
console.log(xhr.responseXML);
}else{
console.log("请求失败:" + xhr.status);
}
}
};
xhr.open("get","example.php",true);
// xhr.overrideMimeType("text/plain");
xhr.overrideMimeType("text/xml");
xhr.send(null);

如果服务器响应没有指定一个Content-Type头,某些浏览器不能正常工作;另外,如果没有指定,XMLHttpRequest默认MIME类型为”text/xml”,如果接受的数据不是有效的XML,将会出现”格式不正确“的错误,此时就可以通过调用 overrideMimeType() 指定各种类型来避免这种情况;

如果响应的数据不能转换为overrideMimeType指定的类型,那responseXML将返回null;

IE10及以下不支持该方法;

进度事件:

Progress Events规范是W3C的一个工作草案,定义了与客户端服务器通信有关的事件,这些事件最早只针对XHR操作,但目前也被其他API借鉴;其有以下进度事件:

  • loadstart:在接收到响应数据的第一个字节时触发;
  • progress:在接收响应期间持续不断地触发;
  • error:在请求发生错误时触发;
  • abort:在国为调用abort()方法而终止连续时触发;
  • timeout:超过时触发
  • load:在接收到完整的响应数据时触发;
  • loadend:在通信完成或者触发error、abort或load事件后触发;
var xhr = createXHR();
function xhrEvent(event){console.log(event.type)}
xhr.onloadstart = xhrEvent;
xhr.onprogress = xhrEvent;
xhr.onerror = xhrEvent;
xhr.onabort = xhrEvent;
xhr.ontimeout = xhrEvent;
xhr.onload = xhrEvent;
xhr.onloadend = xhrEvent;
xhr.open("get","example.php",true);
xhr.send(null);

IE9只支持load事件,IE9以下不支持所有事件;

HTTP请求无法完成有3种情况,对应3种事件;如果请求超时,会触发timeout事件;如果请求中止,会触发abort事件;如果是其他错误,例如网络错误会阻止请求完成,此时就会触发error事件;

对于任何具体的请求,浏览器只会触发load、abort、timeout和error事件中的一个;XHR2规范规定,只要以上这些事件中的一个触发后,都会触发loadend事件;在使用这些事件前,最好检测浏览器是否支持这些事件,例如:

if ("onprogress" in (new XMLHttpRequest)) {
xhr.onprogress = xhrEvent;
}

load加载事件:

Firefox最早引入了load事件,用以替代readystatechange事件;响应接收完毕后将触发load事件,因此也没有必要检查readyState属性;而onload事件处理程序会接收到一个event对象,其target属性就指向XHR对象实例,因而可以访问XHR对象的所有方法和属性,标准浏览器都支持load事件,如:

xhr.onload = function(event){
var target = event.target;
console.log(target.status); // 200
console.log(target.readyState); // 4
};
xhr.open("get","example.php",true);
xhr.send(null);

只要浏览器接收到服务器的响应,不管其状态如何,都会触发load事件,所以必须要检查status属性,才能确定数据是否真的已经可用了;如:

xhr.onload = function(event){
var target = event.target;
if(target.status == 200){
console.log(target.responseText);
}else{
console.log("请求失败:" + target.status);
}
};

不是所有浏览器都为这个事件实现了适当的事件对象,所以最终还是引用XHR对象本身,如:

xhr.onload = function(event){
if(xhr.status == 200){
console.log(xhr.responseText);
}else{
console.log("请求失败:" + xhr.status);
}
};

示例:读取外部文件

function successHandler(){
this.callback.apply(this, this.arguments);
}
function errorHandler(){
console.log(this.statusText);
}
function loadFile(url, callback /*, arg1, arg2,...*/){
var xhr = new XMLHttpRequest();
xhr.callback = callback;
xhr.arguments = Array.prototype.slice.call(arguments, 2);
xhr.onload = successHandler;
xhr.onerror = errorHandler;
xhr.open("GET", url, true);
xhr.send(null);
}
// 用法
function showMessage(message){
console.log(message + this.responseText);
}
loadFile("example.txt", showMessage, "新信息:\r\n");

progress进度事件:

Mozilla对XHR添加了progress事件,这个事件会在浏览器接收到新数据期间周期性的触发,通常频率是50毫秒左右,因此可以使用这个事件给用户反馈请求的进度;如果请求快速完成,有可能不会触发该事件;

xhr.onprogress = function(event){
console.log(event); // ProgressEvent
};

ProgressEvent:

是测量如HTTP请求(一个XMLHttpRequest,或者一个<img>、<audio>、<video>、<style>或<link> 等底层资源的加载)等底层流程进度的事件;其继承自Event类;

其event对象包含着三个额外的属性:

  • lengthComputable:表示进度信息是否可用的布尔值
  • loaded:当前已经接收的字节数(当使用HTTP下载资源,它只表示内容本身的部分,不包括首部和其它开销);
  • total:表示响应数据的总字节数,其是根据Content-Length响应头部确定的预期字节数;

可以使用loaded和total来计算数据接收比例,例如,可以为用户创建一个进度指示器,如:

var divStatus = document.getElementById("divStatus");
xhr.onprogress = function(event){
if(event.lengthComputable){
divStatus.innerHTML = "完成率:" + Math.round(event.loaded / event.total * 100) + "%,已接收:" + event.loaded + "字节,共:" + event.total + "字节";
}
};

如果头部信息中包含Content-Length字段,也可用它来获取响应数据的大小;

中止请求:
当在XMLHttpRequest对象上调用abort()方法时,会触发abort事件;调用abort()的主要原因是完成取消或超时请求消耗的时间太长或当响应变得无关时

超时设定:
IE为XHR对象添加了一个timeout属性,表示请求在等待响应多少毫秒之后终止,如果在规定时间内没有接收到响应,会触发timeout事件;该属性默认为0,代表没有超时;在IE中,timeout属性只能在调用open()之后且在调用send()方法之前设置,如:

var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
try {
if (xhr.status == 200) {
console.log(xhr.responseText);
}else{
console.log("请求失败:" + xhr.status);
}
} catch (ex) {
console.log("不处理了");
// 此处就不用实现了,由ontimeout事件处理程序处理
}
}
};
xhr.open("get","example.php",true);
xhr.timeout = 1000;
xhr.ontimeout = function(){
alert("在1秒内没有返回值");
}
xhr.send(null);

timeout属性不能用在同步的XMLHttpRequest请求中,否则会抛出InvalidAccessError类型的错误;如:

function loadFile(url, timeout, callback){
var args = Array.prototype.slice.call(arguments, 3);
var xhr = new XMLHttpRequest();
xhr.ontimeout = function(){
console.error("请求:" + url + ",超时");
};
xhr.onload = function(){
if(xhr.readyState == 4){
if(xhr.status == 200)
callback.apply(xhr, args);
else
console.error(xhr.statusText);
}
};
xhr.open("GET", url, true);
xhr.timeout = timeout;
xhr.send(null);
}
function showMessage(message){
console.log(message + this.responseText);
}
loadFile("example.txt", 2000, showMessage, "请消息:\r\n");

某些旧版本的浏览器不支持timeout属性及ontimeout事件,但可以使用setTimeout()和about()方法模拟超时,如:

function timedGetText(url, timeout, callback){
var xhr = new XMLHttpRequest();
var timeouted = false; // 是否超时
// 启动计时器,在timeout毫秒后将中止请求
var timer = setTimeout(function(){
timeouted = true;
xhr.abort();
console.log("超时了,不玩了");
}, timeout);
xhr.open("GET", url, true);
xhr.onreadystatechange = function(){
if(xhr.readyState !== 4) return; // 忽略未完成的请求
if(timeouted) return; // 忽略中止请求

clearTimeout(timer); // 取消等待的超时
if(xhr.status == 200)
callback(xhr.responseText);
};
xhr.send(null);
}
timedGetText("wait.php", 2500, function(data){
console.log(data);
});

wait.php:

<?php
sleep(2);
echo "零点程序员";

upload属性:

返回一个 XMLHttpRequestUpload对象,用来表示上传的进度,其继承XMLHttpRequestEventTarget类型,可以通过对其绑定事件来追踪它的进度;

console.log(xhr.upload); // XMLHttpRequestUpload

注册在upload对象上的事件如下:

  • loadstart:获取开始
  • progress:数据传输进行中
  • abort:获取操作终止
  • error:获取失败
  • load:获取成功
  • timeout:获取操作在用户规定的时间内未完成
  • loadend:获取完成(不论成功与否)
function uploadHandler(e){console.log(e)}
xhr.upload.onloadstart = uploadHandler;
xhr.upload.onload = uploadHandler;
xhr.upload.onerror = uploadHandler;
xhr.upload.onabort = uploadHandler;
xhr.upload.onprogress = uploadHandler;
xhr.upload.ontimeout = uploadHandler;
xhr.upload.onloadend = uploadHandler;

upload对象的progress事件:

XMLHtttpRequest对象的upload属性指向一个对象,即上传对象,其传送数据的时候,会触发progress事件,用来返回进度信息;

progress事件会分成上传和下载两种情况:
下载的progress事件属于XMLHttpRequest对象;上传的progress事件属于XMLHttpRequest.upload对象;两者同属于一种类型,拥有相同的成员;

upload的progress事件用来跟踪上传进度,如:

xhr.upload.onprogress = function(e){
if (e.lengthComputable) {
var percentComplete = e.loaded / e.total;
console.log(percentComplete);
}
};

示例:拖拽文件上传进度,如:

<style>
.fileDropTarget{width:300px; height:200px; background-color: lightblue;margin:20px; float: left;}
.wantdrop{}
.uploading{}
</style>
<div class="fileDropTarget" data-uploadto="uploads"></div>
<div class="fileDropTarget" data-uploadto="uploads"></div>
<div class="fileDropTarget" data-uploadto="uploads"></div>
<div class="fileDropTarget" data-uploadto="uploads"></div>
<script>
function createXHR(){
if (typeof XMLHttpRequest != "undefined") {
return new XMLHttpRequest();
}else if (typeof ActiveXObject != "undefined") {
if(typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"];
for(var i=0, len=versions.length; i < len; i++){
try{
var xhr = new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
console.log("xhr被创建了");
return xhr;
}catch(ex){
throw new Error("浏览器不支持MSXML");
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}else{
throw new Error("不支持XHR对象");
}
}

var elts = document.getElementsByClassName("fileDropTarget");
for(var i=0; i<elts.length; i++){
var target = elts[i];
var url = target.getAttribute("data-uploadto");
if(!url) continue;
createFileUploadDropTarget(target, url);
}
function createFileUploadDropTarget(target, url){
var uploading = false;
console.log(target, url);
target.ondragenter = function(e){
console.log("dragenter");
if(uploading) return; 
var types = e.dataTransfer.types
if(types &&
((types.contains && types.contains("Files")) ||
(types.indexOf && types.indexOf("Files") !== -1))){
target.classList.add("wantdrop");
return false;
}
};

target.ondragover = function(e){
if(!uploading) return false;
};
target.ondragleave = function(e){
if(!uploading) target.classList.remove("wantdrop");
};
target.ondrop = function(e){
if(uploading) return false;
var files = e.dataTransfer.files;
if(files && files.length){
uploading = true;
var message = "Uploading files:<ul>";
for(var i=0; i<files.length; i++)
message += "<li>" + files[i].name + "</li>";
message += "</ul>";

target.innerHTML = message;
target.classList.remove("wantdrop");
target.classList.add("uploading");
var xhr = new XMLHttpRequest();
xhr.open("POST", url);
var body = new FormData();
for(var i=0; i<files.length; i++)
body.append(i, files[i]);
xhr.upload.onprogress = function(e){
if(e.lengthComputable){
target.innerHTML = message +
Math.round(e.loaded/e.total*100) +
"% Complete";
}
};
xhr.upload.onload = function(e){
upload = false;
target.classList.remove("uploading");
target.innerHTML = "Drop files to upload";
};
xhr.send(body);
return false;
}
target.classList.remove("wantdrop");
}
}
</script>
第68节 Ajax-Web前端开发之JavaScript-零点程序员-王唯「建议收藏」

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/6960.html

(0)
上一篇 2023-01-03 09:54
下一篇 2023-01-03 09:54

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信