AJAX入门

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。
其实简单点说,AJAX 就是 JS 中的一个对象而已,就是 XMLHttpRequest对象

同步与异步

AJAX 是一种异步加载的技术多使用在表单提交、(滚动)加载更多、输入提示等方面

先来了解下同步请求是什么,比如提交一个表单,流程可以是:
点击提交后,客户端向服务器发起请求,服务器进行处理,然后返回结果给客户机,然后客户端刷新页面显示结果;
在客户端发起请求后,是一直处于等待服务器响应的状态的,没法做别的事,这是同步的一种体现

还是上面的例子,如果是异步请求,那就是在你输入某一个信息后,就会携带某个信息立即向服务器发起请求,服务器返回结果,从而调用 js/css 来显示给用户输入是否正确(比如用户名是否重复)
在这个过程用户完全可以继续填其他的内容,也不会刷新页面,这就是异步的一种体现

向服务器发送请求

前面我们说过,AJAX 的核心就是 XMLHttpRequest 对象,所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。

XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

1
2
3
4
5
6
7
8
9
10
11
var xmlhttp=new XMLHttpRequest();
// 参数为:请求方式、请求地址、是否异步(默认为异步)
xmlhttp.open("GET","test1.action",true);
// 发送请求
xmlhttp.send();

xmlhttp.open("POST","ajax_test.action",true);
// 设置 HTTP 头,表明这是个表单数据
// 使用post方式必须设置,在open和send之间
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("fname=Bill&lname=Gates");

注意 send 方法,如果是 GET 请求,因为参数都在 URL 中,所以不需要填(填了也没用),如果是 POST 请求,那么就必须把参数传入 send 方法了

服务器响应

对于对象来说,响应就很简单了,ajax 支持三种类型的数据:

  • String
  • XML
  • JSON

其中 String 和 JSON 的 MIME 类型都是 text/plain 都是使用 responseText 来接收;而 XML 类型的数据使用 responseXML 来接收,MIME 是 text/xml 在服务端设置的时候需要注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获得字符串形式的响应数据。
xmlhttp.responseText
// 获得 XML 形式的响应数据。
xmlhttp.responseXML
// 可以直接这样获取,和html类似,但是不能用 innerHTML 方法
// ajax.responseXML.getElementsByTagName('root').firstChild.nodeValue;

// 获取数字或文本形式的 HTTP 状态码
xmlhttp.status
xmlhttp.statusText
// 获取所有的响应报头
xmlhttp.getAllResponseHeader()
// 查询响应中的某个字段的值
xmlhttp.getResponseHeader()

我们可以通过监听 readyState 属性来实现:

readyState 属性的变化代表服务器响应的变化
0:请求未初始化,open还没有调用
1:服务器连接已建立,open已经调用了
2:请求已接收,也就是接收到头信息了
3:请求处理中,也就是接收到了响应主体
4:请求已完成,且响应已就绪,也就是响应完成了

1
2
3
4
5
6
7
8
9
var request = new XMLHttpRequest();
request.open("GET","get.php",true);
request.send();
// 设置监听,每当 readyState 改变时,就会触发 onreadystatechange 事件
request.onreadystatechange=function(){
if(request.readState===4 && request.status===200){
//做一些事情 request.responseText;
}
}

需要注意的是,不同的浏览器对这几种状态码的支持是不一样的,也就是说有的浏览器没有 0,有的没有 1;但是肯定都支持 4;
并且只有在状态码发生变化后才会触发这个函数,所以说如果状态码一直是 4 也不会触发这个函数

解析XML

如果是 XML 的数据,那么解析的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function callback2() {
if(ajax.readyState == 4){
if(ajax.status == 200){
var div = document.getElementById('show');
var ruslt = ajax.responseXML;
var str = ["<table style='width: 600px;margin:0 auto;' border='1px'><tr><th>ID</th><th>姓名</th><th>邮箱</th><th>手机</th></tr>"];

var stus = ruslt.getElementsByTagName('student');
var len = stus.length;
for (var i = 0; i < len; i++) {
str.push("<tr><td>" + stus[i].getAttribute('id') + "</td>");
str.push("<td>" + stus[i].getAttribute('name') + "</td>");
// stus[i].getElementsByTagName('email')[0].firstChild.nodeValue
str.push("<td>" + stus[i].childNodes[0].textContent + "</td>");
str.push("<td>" + stus[i].childNodes[1].textContent + "</td></tr>");
}
str.push("</table>");

div.innerHTML = str.join("");
}
}

其中,for 循环为了避免每次都判断 length 属性浪费性能,所以单独提出来,如果对顺序没要求,可以直接采用倒序遍历的方式,就没有这个问题了。
为了避免字符串拼接的效率问题,使用数组来代替字符串的拼接,主要是 push 方法增加,最后使用 join("") 转成字符串,如果直接使用 toString 方法那会输出 abc,def,aa,xx ,所以使用 join 来处理。

解析JSON

JSON 类型的数据应该是最常见的,同时解析也非常的简单,因为 JSON 的语法和 JS 的对象定义基本完全一致的。
一般原生 JS 解析 JSON 可以使用两种方式:

  • JSON.parse(str)
  • eval('(' + str + ')')

后面的遍历就简单了,完全按照 JS 中的对象处理来。

用jQuery实现

jQuery 已经帮我们封装好了,使用起来也非常的方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 发送数据
$(document).ready(function() {
$("#save").click(function() {
$.ajax({
type: "post",
url: "service.php",
dataType: "json",
data: {
name: $("#staffName").val(),
number: $("#staffNumber").val(),
sex: $("#staffSex").val(),
job: $("#staffJob").val()
},
// 必须是一个方法,成功后回调
success: function(data) {
if (data.success) {
$("#createResult").html(data.msg)
} else {
$("#createResult").html("error: " + data.msg)
}
},
error: function(jqXHR) {
alert("error: "
jqXHR.status);
}
});
});
});

// 获取数据
$.ajax({
type: "GET",
url: "service.php?number=" + $('#keyword').val,
dataType: "json",
// 必须是一个方法,成功后回调,data 是 Obj 类型
success: function(data) {
if (data.success) {
$("#createResult").html(data.msg)
} else {
$("#createResult").html("error: " + data.msg)
}
},
error: function(jqXHR) {
alert("error: "
jqXHR.status);
}
});

data :是一个对象,连同请求发送到服务器的数据
dataType:预期服务器返回的数据类型,如果不指定将根据 HTTP 包中的 MIME 信息智能判断,一般我们都用 JSON 格式

如果是 Json 类型的数据还可以使用 JavaScript 原生的 JSON.parse 方法进行转换成对象,jQuery 中使用 ajax 请求的方式除了上面的栗子有下面几种:

  • jQuery.load( url, [data], [callback] )
    默认使用 GET 方式来传递的,如果 [data] 参数有传递数据进去,就会自动转换为 POST 方式的。
  • jQuery.get( url, [data], [callback] )
    相似的还有一个 $.getJSON() 方法,只是被限定为 JSON 类型
  • jQuery.post( url, [data], [callback], [type] )
  • jQuery.getScript( url, [callback] )
    通过 GET 方式请求载入并执行一个 JavaScript 文件

$.ajax() 是所有 ajax 方法中最底层的方法,所有其他方法都是基于 $.ajax() 方法的封装,jQuery 真的是博大精深啊
callback 函数可以有三个参数,第一个就是服务器返回是数据了(对象形式,和服务器端的实体对象对应),第二个是状态码,第三个是 ajax 对象。

跨域

在浏览器的规则中 JavaScript 是不被允许访问其他域下的内容的,只有在子域名和主域名都相同的情况下才不算跨域,端口号也必须相同
http 与 https 之间也算是跨域

解决这个问题,一般的几种方式为:

使用代理

属于后端技术,比如写个 PHP 页面专门用来转发请求,后端访问是没有问题的

JSONP (只支持GET请求)

出于安全考虑,浏览器都有同源策略。即相同 domain(域) 的页面运行在一个沙箱(sandbox)中,与其他 domain 的沙箱隔离,不能跨越 domain 直接访问其他 domain 下的资源。

但HTML中有几个标签可以忽略同源限制去请求其他 domain 下的资源,比如<img><script>等。比如当浏览器解析到<script>标签,就会发起一个get请求,请求的 URL 即为 scr 所指定的 url。这就相当于跨域访问了一个资源。

JSONP 就是这样的原理,我们可以利用 Src 来跨域得到我们想要的数据,但这这样就会变成下面的样子

1
2
3
<script>
{['some string 1', 'some data', 'whatever data']}
</script>

但这样的数据解析很麻烦,所以 JSONP 做了下处理,返回的结果是:
my_callback({['some string 1', 'some data', 'whatever data']});
可以看到,这里的返回结果是直接执行了一个函数 my_callback(...), 实参就是我们需要的数据;那么只要在代码里实现 my_callback 函数,就可以做任何想做的事了

当然也可以使用 jQuery 来做,type 选择 jsonp,然后增加一个属性 jsonp,值可以任意,其实就是上面的那个函数名

然后在跨域的服务端要获取到这个名字,可以看出是在 url 传递了一个参数,所以也就只支持GET请求了,比如:

1
2
3
4
5
6
$.ajax({
type:"GET",
url:"http://127.0.0.1:8080/ajaxdemo/service.php?number"+$("#keyword").val(),
dataType:"jsonp", //由"json"改为"jsonp"
jsonp:"callback", //增加此项,用于后台代码编写
....

后端代码以 PHP 为例

1
2
3
4
5
$jsonp = $_GET["callback"];

// 返回值我们说过应该是一个函数,函数名就是上面的jsonp变量了
// 所以要改在一下,外面套一层函数
$result = $jsonp.'(data..)'

简单说就是在 script 标签里我们请求 :<script src=‘http://b.com?callback=fun’ /> ,为了方便后端返回把函数名当做参数传回去了,后端拿到这个参数就相当于拿到了回调函数的名称,然后拼一个函数把数据当做参数传入就行了(fun('datadatadata')),这样前台就能直接调用了,方法的定义在前台
并且后端处理更灵活,如果发现没带 callback 参数那就返回正常的 JSON 数据,如果带了就返回 JSONP 的格式,一个接口适应了两种情况

XHR2

HTML5 提供的 XMLHttpRequest Level2 已经实现了跨域访问以及其他的一些新功能

服务器端加入下面两句

那个域可以访问,* 表示所有
header(‘Access-Control-Allow-Origin:*‘);
支持什么方法
header(‘Access-Control-Allow-Methods:POST,GET’);

其他补充

异步请求使用一个 XMLHttpRequest 对象就足够了,发起的异步请求都会交给它来处理,它内部必然有个类似数组结构的变量来存,然后依次请求服务器(将原始请求进行包装,然后以 http 协议发送),当响应完成后(通过流的形式)再由 XMLHttpRequest 通知调用方(返回给调用方)进行处理;
Ajax 其实就是起到了一个中转的作用


利用 js 将 select 元素的 options 的长度设为 1,会只保留第一项其他的全部清空,比如: selectElement.options.length = 1


ajax 技术还经常用在验证码的时候,判断输入的个数以及按键弹起事件(onkeyup)通过后,通过 ajax 从后台获取输入的是否正确,及时给予用户提示

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~