第三十五课:Ajax详解
一个完整的Ajax请求:
var xhr = new (self.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP"); //new XMLHttpRequest()传入参数不影响。
xhr.onreadystatechange = function(){ //请求状态改变就会执行这个函数,状态有0,1,2,3,4五个状态。
if(this.readyState === 4 && this.status === 200){
alert(this.responseText);
}
}
xhr.open("post","url",true); //第三个参数如果为true,就代表异步请求,如果为false就代表是同步。
xhr.setRequestHeader("",""); //设置请求头
xhr.send("key=val&key2=val2"); //发送数据
大家可能知道,在new ActiveXObject()时,针对IE不同版本的浏览器,传入的参数也不一样。根据IE官方建议,我们只要检测这四个参数:"Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP",Microsoft.XMLHTTP。其中"Microsoft.XMLHTTP"是针对IE5以及以前版本的。
为了达到兼容性,我们需要这样做:
var xhr = (function(){
var fns = [
function(){return new XMLHttpRequest();},
function(){return new ActiveXObject("Msxml2.XMLHTTP");},
function(){return new ActiveXObject("Msxml2.XMLHTTP.6.0");},
function(){return new ActiveXObject("Msxml2.XMLHTTP.3.0");},
function(){return new ActiveXObject("Microsoft.XMLHTTP");} //这里兼容IE5,现在的程序都不需要兼容IE5了,因此可以去掉。
], xhrTemp;
for(var i =0,len = fns.length ; i< len;i++){
try{
fns[i](); //调用每一个创建xhr对象的函数,如果成功,就把生成xhr对象的函数返回给xhr变量。
xhrTemp = fns[i];
break;
}catch(){}
}
return xhrTemp;
})();
还有一部分人可能知道,其实IE6,7已经支持XMLHttpRequest对象了,但是为什么还需要用ActiveXObject对象呢,因为IE6,7的XMLHttpRequest对象有兼容性问题,比如:IE7下的xhr对象不支持本地file协议,会出现拒绝访问,需要倒退到ActiveXObject对象来操作本地file协议的功能。而且它们的xhr对象不是一个javascript对象,也就是说在IE6,7下,你通过var xhr = new XMLHttpRequest();生成的xhr对象不是js对象,而是ActiveXObject对象。
总结以上,我们可以在我们框架中,写出以下的最终版本:
window.$ = {};
var s = ["XMLHttpRequest", "ActiveXObject('Msxml2.XMLHTTP.6.0')", "ActiveXObject('Msxml2.XMLHTTP.3.0')", "ActiveXObject('Msxml2.XMLHTTP')"];
if(!"1"[0]){ //IE6,7下,"1"[0]返回undefined,其他浏览器返回"1"。
s[0] = location.protocol === "file:" ? "!" : s[0]; //如果是本地协议的操作,那么就覆盖XMLHttpRequest,解决IE6,7兼容性问题。
}
for(var i =0, xhr; xhr = s[i++];){
try{
if(eval("new " +xhr)){ //判断是否能够new出一个xhr对象,这里的eval可以把字符串解析成js代码执行
$.xhr = new Function("return new " + xhr); //新建一个Function实例对象,传入的参数是一个字符串。如果xhr="XMLHttpRequest",这段代码实际上会变成这样:function(){ return new XMLHttpRequest();}
break;
}
}
catch(){}
}
xhr对象的事件回调函数的绑定与状态分析
针对xhr对象的事件回调函数,我们一般使用onreadysatechange。但是XMLHttpRequest2(XMLHttpRequest新版本)添加了多个事件回调函数。如下:
loadstart:在请求开始时触发
progress:在请求发送数据以及接收数据期间,在服务器指定的时间,间隔触发
abort:在请求被取消时触发,例如:在调用了xhr.abort()方法时。
error:在请求失败时触发
load:在请求成功时触发
timeout:超时触发
loadend:在请求完成时触发,无论是成功还是失败
对于xhr请求的状态,我们一般通过判断xhr.status的值。
2xx状态与304我们都可以看成是成功状态。但是有一些兼容性问题需要处理:IE(非原生的xhr对象,也就是使用ActiveXObject实现的xhr)中,会将204设置为1223,Opera会在取得204后将status设置为0。因此,成功状态的写法如下:
if((xhr.status >=200 && xhr.status < 300) || xhr.status ==304 || xhr.status ==1223 || xhr.status ==0)
还有一个特例:在Firefox下,本地使用XMLHttpRequest,成功返回时,status等于0。由于很少在本地发请求,因此很多框架都没有对这个进行处理。
xhr发送请求和数据
open方法有5个参数,open(method,url,async,username,password)
method代表请求的HTTP方法,值包括:GET,POST,PUT,DELETE.HEAD,OPTION等。有些浏览器还支持你自定义method。
url是请求的地址,浏览器对这个url会进行同源安全策略,也就是会要求这个URL与包含脚本的文件具有相同的域名,端口,协议。在GET请求下,我们需要把传给url的数据,放到url?的后面,只能是字符串。
async指示请求使用的是异步还是同步,如果这个参数是false,请求是同步的,那么只要在send方法后,取xhr的响应数据就行了。如果这个参数是true或省略,请求是异步的,那么就需要在open方法之前在xhr对象上添加一个onreadystatechange事件回调函数。
username,password参数是可选的,为url所需的授权提供认证资格。
发送数据时,我们使用的是send方法,get方法一般传入null(在url?后面已经传入数据了),而post请求传入你要发给此url的数据。
早期的xhr对象,send方法中只能传入字符串,现代的xhr可以传FormData,document,ArrayBuffer,Blob。
FormData是一个什么样的对象呢,大家都知道,我们点击按钮提交表单时,浏览器会自动将表单的所有disabled为false的input,textarea,select,button元素的name与value抽取出来,变成一个querystring字符串。但是,我们在做ajax请求时,浏览器不会自动将表单的所有这些元素进行这样的处理,因此,我们需要用代码手动来实现这样的功能。jQuery中的serialize方法就是实现这样功能的一个方法。而现代浏览器的FormData对象也可以实现这样的功能。举个例子:
var formdata = new FormData();
formdata.append("name", "chaojidan");
formdata.append("age", 26);
xhr.send(formdata);
FormData也可以这样用:
var formobj = document.getElementById("form"); //得到页面上form元素的对象。
var formdata = formobj.getFormData(); //得到此form表单中所有disabled为false的input,textarea,select,button元素的name与value。
xhr.send(formdata);
FormData还可以这样用:
var formobj = document.getElementById("form"); //得到页面上form元素的对象。
var formdata =new FormData(formobj); //得到此form表单中所有disabled为false的input,textarea,select,button元素的name与value。
xhr.send(formdata);
如果send的是一个document,我们需要把这个document赋值为一个xml对象,然后send(document)。
至于ArrayBuffer和Blob对象,大家可以去看js高级程序编程,里面说的很详细。
xhr接收数据
早期的xhr对象拥有两种接收数据的属性,xhr.responseText对应解码后的字符串(默认解码为utf-8),xhr.responseXML对应一个XML文档。IE还支持xhr.responseBody对应未解码的二进制数据。现在流行json数据的传输,因此服务器会通过responseText传json格式的字符串给前端,而我们前端需要对这个json格式的字符串进行解析,解析成js对象。至于如何解析,大家可以去看ajax hacks这本书。
至于后端返回什么类型的数据,我们可以通过xhr.getResponseHeader("Content-Type");
XMLHttpRequest2新增了responseType和response属性。
responseType,在发送请求前,根据你的数据需要,可以将xhr.responseType设置为text,arraybuffer,blob或document。忽略此设置,默认将响应设为text。
在请求完成后,xhr.response的属性值为DOMString或ArrayBuffer或Blob或Document,具体取决于responseType的设置。
xhr还有一个overrideMimeType方法,比如:xhr.overrideMimeType("text/plain; charset = x-user-defined"),此段代码的意思是,重写了默认的MIME类型,强制浏览器将该响应当成纯文本文件来对待,并且使用一个用户自定义的字符集(这样就是告诉浏览器,不要去解析数据,直接返回未处理过的字节码)。
XMLHttpRequest实现文件上传
假设页面上有一个id为upload的input(type = file),还有一个id为progress用于显示进度的SPAN元素。
window.addEventListener("load", function(){
var el = document.querySelector("#upload");
var progress = document.getElementById("#progress");
el.addEventListener("change",function(){
var file = this.files[0]; //点击input,会弹出一个选择文件的框,用户选择好文件后,点击确定按钮,这时就会触发input元素的change事件,并且input元素有一个files属性,此属性值就是用户要上传的文件数组。input.files[1]代表第二个文件。
if(file){
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress",function(){ //当用ajax请求发送文件时,会触发xhr.upload的progress事件
var done = e.position || e.loaded, total = e.totalSize || e.total; //兼容性写法
progress.innerHTML = (Math.floor(done/total *1000)/10) + "%"; //用百分比的形式显示,文件已经发送了百分之多少?
});
xhr.addEventListener("load",function(){ //当ajax请求完成后,会触发load事件。
progress.innerHTML = "上传成功";
});
xhr.open("put", "/upload", true); //打开这个请求,异步请求
xhr.setRequestHeader("X-File-Name", encodeURIComponent(file.fileName || file.name)); //把文件的名字放到请求头的X-File-Name字段中,需要编码
xhr.setRequestHeader("Content-Type", "application/octet-stream"); //设置ajax请求的数据类型
xhr.send(file); //发送文件。
}
});
})
如果文件太大,我们可以用文件对象的slice方法对文件进行切割,然后分块上传。比如:上面例子中的file,我们可以调用var file1 = file.slice(0,1024),上传file1,然后再var file2 = file.slice(1024,file.size),上传file2。file.size可以其实就是得到file的文件大小。
那么,如何在老版本IE下上传文件,由于我们无法序列化二进制文件(上传文件其实就是二进制数据的上传),只好通过传统的form提交方法实现,为了防止提交后,刷新本页面,我们需要创建一个临时的iframe,具体实现方案,我们将在下一课代码讲解。
加油!
第三十五课:Ajax详解的更多相关文章
- NeHe OpenGL教程 第三十五课:播放AVI
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- Java进阶(三十二) HttpClient使用详解
Java进阶(三十二) HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们 ...
- 【Java入门提高篇】Day34 Java容器类详解(十五)WeakHashMap详解
源码详解系列均基于JDK8进行解析 说明 在Java容器详解系列文章的最后,介绍一个相对特殊的成员:WeakHashMap,从名字可以看出它是一个 Map.它的使用上跟HashMap并没有什么区别,所 ...
- 【Java入门提高篇】Day33 Java容器类详解(十五)PriorityQueue详解
今天要介绍的是基础容器类(为了与并发容器类区分开来而命名的名字)中的另一个成员——PriorityQueue,它的大名叫做优先级队列,想必即使没有用过也该有所耳闻吧,什么?没..没听过?emmm... ...
- Django(十五)模板详解:模板标签、过滤器、模板注释、模板继承、html转义
一.模板的基础配置及使用 [参考]https://docs.djangoproject.com/zh-hans/3.0/topics/templates/ 作为Web框架,Django提供了模板,用于 ...
- 夯实Java基础(二十五)——JDBC使用详解
1.JDBC介绍 JDBC的全称是Java Data Base Connectivity(Java数据库连接).是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问(例如MyS ...
- shell学习三十四天----printf详解
http://blog.csdn.net/shanyongxu/article/details/46744055
- 潭州课堂25班:Ph201805201 django 项目 第三十五课 后台用户权限的添加 mixins 课堂笔记)
验证用户登录: 对一个视图函数进行登录权限验证,(登录后才可以访问,否则重定向到登录页面) #from django.contrib.auth.decorators import login_requ ...
- python第三十五课——生成器
1.生成器: 什么是生成器? 它内部封装了一套公式/算法,只有等到需要调用/执行数据时 --> next()函数执行 才会将公式计算得到数据结果,这就是生成器的原理(核心思想): [注意事项]: ...
随机推荐
- poj 1664 放苹果,递归(深度优先搜索)
#include "stdio.h" int DFS(int n,int m); int main() { int T; int n,m; int s; scanf("% ...
- Azure CDN 启用HTTPS
默认情况下,订阅里的CDN不支持HTTPS 需要联系21v,提交工单告知订阅号,由后台为订阅启用CDN的HTTPS功能. 然后就可以在创建CDN时选到HTTPS的选项了.
- Window I/O 完成端口 (Windows I/O Completion Port (IOCP))
相关对象 IO EndPoint, 所有支持重叠IO(overlapped IO)的设备,比如文件,Winsock,管道等. IOCP, IO完成端口内核对象,可以使用API CreateIoComp ...
- Git指令总结和图表
Git 是一个很强大的分布式版本控制系统.它不但适用于管理大型开源软件的源代码,管理私人的文档和源代码也有很多优势. Git常用操作命令: 1) 远程仓库相关命令 检出仓库:$ git clone g ...
- java自带命令工具
jstat,这个工具很强大,可以监测Java虚拟机GC多方面的状态,具体参数含义参见此链接: ./jstat -gc 84012 1000 3 S0C S1C S0U S1U ...
- Linux平台Java调用so库-JNI使用例子
1.确保gcc编译器已安装 2.编写HelloJNI.java代码,用native声明需要用C实现的函数.如果源程序是包含在package里的话,应该建立同样的文件夹结构,比如/home/swan/t ...
- java 21 - 4 字符流的文件复制操作以及简化
既然字节流可以复制文件,那么字符流当然也有. 同样的思路: 数据源: a.txt -- 读取数据 -- 字符转换流 -- InputStreamReader目的地: b.txt -- 写出数据 -- ...
- Android 界面排版的5种方式
Android布局是应用界面开发的重要一环,在Android中,共有五种布局方式,分别是:FrameLayout(框架布局),LinearLayout (线性布局),AbsoluteLayout(绝对 ...
- 集锦.txt
不同的时代,有不同的需要以前,我们都以为自己可以为爱情去死,可是后来爱情死了,我们还活着. 小时候男孩子喜欢电动玩具,女孩子喜欢娃娃.长大了男孩子喜欢娃娃,女孩子喜欢电动玩具.也许这就是成长吧! 我大 ...
- Sql复习之安全性与权限管理+vmware增加硬盘容量
参考资料: http://www.cnblogs.com/Jackeyzhang/archive/2011/05/18/2049621.html VmWare虚拟机增加硬盘容量的方法 http://b ...