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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
---
title: Getting Started
slug: Web/Guide/AJAX/Getting_Started
translation_of: Web/Guide/AJAX/Getting_Started
---
<div>{{DefaultAPISidebar("XMLHttpRequest")}}</div>
<p class="summary">这篇文章旨在帮助你了解AJAX基础,并提供两个可上手的简单案例供你学习。</p>
<h2 id="什么是AJAX?">什么是AJAX?</h2>
<p>AJAX是异步的JavaScript和XML(<strong>A</strong>synchronous <strong>J</strong>avaScript <strong>A</strong>nd <strong>X</strong>ML)。简单点说,就是使用 <code><a href="/en/DOM/XMLHttpRequest">XMLHttpRequest</a></code> 对象与服务器通信。 它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。AJAX最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。</p>
<p>你可以使用AJAX最主要的两个特性做下列事:</p>
<ul>
<li>在不重新加载页面的情况下发送请求给服务器。</li>
<li>接受并使用从服务器发来的数据。</li>
</ul>
<h2 id="Step_1_–_怎样发送http请求">Step 1 – 怎样发送http请求</h2>
<p>为了使用JavaScript向服务器发送一个http请求,你需要一个包含必要函数功能的对象实例。这就是为什么会有 <code>XMLHttpRequest</code> 的原因。 这是IE浏览器的ActiveX对象 <code>XMLHTTP</code>的前身。 随后Mozilla,Safari和其他浏览器也都实现了类似的方法,被称为 <code>XMLHttpRequest</code> 。同时,微软也实现了XMLHttpRequest方法。</p>
<pre class="brush: js">// Old compatibility code, no longer needed.
if (window.XMLHttpRequest) { // Mozilla, Safari, IE7+ ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 6 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
</pre>
<div class="note"><strong>Note: 上面代码只是简单版的如何创建一个XMLHttp实例。更实际的例子,请看本篇文章的step 3。</strong></div>
<p>发送一个请求后,你会收到响应。在这一阶段,你要告诉XMLHttp请求对象是由哪一个JavaScript函数处理响应,在设置了对象的 <code>onreadystatechange </code>属性后给他命名,当请求状态改变时调用函数。</p>
<pre class="brush: js"><code>httpRequest.onreadystatechange = nameOfTheFunction;</code></pre>
<p>要注意的是,函数名后没有参数,因为你把一个引用赋值给了函数,而不是真正的调用了它。 此外,如果不使用函数名的方式,你还可以用JavaScript的匿名函数响应处理的动作,就像下面这样:</p>
<pre class="brush: js">httpRequest.onreadystatechange = function(){
// Process the server response here.
};
</pre>
<p>接下来,声明当你接到响应后要做什么,你要发送一个实际的请求,通过调用HTTP请求对象的 <code>open()</code> 和 <code>send()</code> 方法,像下面这样:</p>
<pre class="brush: js">httpRequest.open('GET', 'http://www.example.org/some.file', true);
httpRequest.send();
</pre>
<ul>
<li><code>open()</code> 的第一个参数是HTTP请求方法 - 有GET,POST,HEAD以及服务器支持的其他方法。 保证这些方法一定要是大写字母,否则其他一些浏览器(比如FireFox)可能无法处理这个请求。更多关于HTTP的请求方法,可以查看 <a class="external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">W3C specs</a>。</li>
<li>第二个参数是你要发送的URL。由于安全原因,默认不能调用第三方URL域名。 确保你在页面中使用的是正确的域名,否则在调用 <code>open()</code> 方法是会有 "permission denied" 错误提示。一个容易犯的错误是你企图通过 <code>domain.tld</code> 访问网站, 而不是使用 <code>www.domain.tld</code>。如果你真的需要向另一个域名发送请求, 可以查看 <a href="/En/HTTP_access_control">HTTP access control</a>。</li>
<li>第三个参数是可选的,用于设置请求是否是异步的。如果设为 <code>true</code> (默认值),即开启异步,JavaScript就不会在此语句阻塞,使得用户能在服务器还没有响应的情况下与页面进行交互。</li>
</ul>
<p><code>send()</code> 方法的参数可以是任何你想发送给服务器的内容,如果是 <code>POST</code> 请求的话。发送表单数据时应该用服务器可以解析的格式,像查询语句:</p>
<pre><code>"name=value&anothername="+encodeURIComponent(myVar)+"&so=on"</code></pre>
<p>或者其他格式, 类似 <code>multipart/form-data</code>,JSON,XML等。</p>
<p>如果你使用 POST 数据,那就需要设置请求的MIME类型。比如,在调用 <code>send()</code> 方法获取表单数据前要有下面这个:</p>
<pre class="brush: js">httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
</pre>
<h2 id="Step_2_–_处理服务器响应">Step 2 – 处理服务器响应</h2>
<p>在发送请求时,你提供的JavaScript函数名负责处理响应:</p>
<pre class="brush: js">httpRequest.onreadystatechange = nameOfTheFunction;
</pre>
<p>这个函数应该做什么?首先,函数要检查请求的状态。如果状态的值是 <code>XMLHttpRequest.DONE</code> (对应的值是4),意味着服务器响应收到了并且是没问题的,然后就可以继续执行。</p>
<pre class="brush: js">if (httpRequest.readyState === XMLHttpRequest.DONE) {
// Everything is good, the response was received.
} else {
// Not ready yet.
}
</pre>
<p>全部readyState状态值都在 <a href="/en-US/docs/Web/API/XMLHttpRequest#Properties">XMLHTTPRequest.readyState</a>,如下也是:</p>
<ul>
<li>0 (未初始化) or (<strong>请求还未初始化</strong>)</li>
<li>1 (正在加载) or (<strong>已建立</strong><strong>服务器链接</strong>)</li>
<li>2 (加载成功) or (<strong>请求已接受</strong>)</li>
<li>3 (交互) or (<strong>正在处理请求</strong>)</li>
<li>4 (完成) or (<strong>请求已完成并且响应已准备好</strong>)</li>
</ul>
<p>(<a class="external" href="http://msdn.microsoft.com/en-us//library/ms534361%28en-us,VS.85%29.aspx">Source</a>)</p>
<p>接下来,点击HTTP响应的 <a href="/en/HTTP#HTTP_Response_Codes">response code</a>。 可能的响应码都已经列在 <a class="external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">W3C</a>这个列表里。在下面的例子中,我们通过检查响应码 <code>200 OK</code> 判断AJAX有没有成功。</p>
<pre class="brush: js">if (httpRequest.status === 200) {
// Perfect!
} else {
// There was a problem with the request.
// For example, the response may have a 404 (Not Found)
// or 500 (Internal Server Error) response code.
}</pre>
<p>在检查完请求状态和HTTP响应码后, 你就可以用服务器返回的数据做任何你想做的了。你有两个方法去访问这些数据:</p>
<ul>
<li><code>httpRequest.responseText</code> – 服务器以文本字符的形式返回</li>
<li><code>httpRequest.responseXML</code> – 以 XMLDocument 对象方式返回,之后就可以使用JavaScript来处理</li>
</ul>
<p>注意上面这一步只在你发起异步请求时有效(即 <code>open()</code> 的第三个参数未特别指定或设为 <code>true</code>)。如果你你发起的是<strong>同步</strong>请求则不必使用函数,但是非常不推荐这样子做,它的用户体验很糟糕。</p>
<h2 id="Step_3_–_一个简单的例子">Step 3 – 一个简单的例子</h2>
<p>让我们把所有的知识都集中起来做一个简单的HTTP请求。这个JavaScript会请求一个HTML文档 <code>test.html</code>,包含 "I'm a test" 内容。然后我们 <code>alert()</code> 响应的内容。注意这个例子我们只是用了JavaScript,没有用jQuery。而且,HTML,XML和PHP文件都要放在用一个目录下。</p>
<pre class="brush: html"><button id="ajaxButton" type="button">Make a request</button>
<script>
(function() {
var httpRequest;
document.getElementById("ajaxButton").addEventListener('click', makeRequest);
function makeRequest() {
httpRequest = new XMLHttpRequest();
if (!httpRequest) {
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
httpRequest.onreadystatechange = alertContents;
httpRequest.open('GET', 'test.html');
httpRequest.send();
}
function alertContents() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
alert(httpRequest.responseText);
} else {
alert('There was a problem with the request.');
}
}
}
})();
</script>
</pre>
<p>在这个例子中:</p>
<ul>
<li>用户点击 “Make a request” 按钮;</li>
<li>事件处理调用 <code>makeRequest()</code> 函数;</li>
<li>请求已通过然后(<code>onreadystatechange</code>)传给 <code>alertContents()</code> 执行。</li>
<li><code>alertContents()</code> 检查返回的响应是否OK,然后 <code>alert()</code> <code>test.html</code> 文件内容。</li>
</ul>
<div class="note"><strong>Note</strong>: 如果你向一个代码片段发送请求,将返回XML,而不是静态XML文件,在IE浏览器上则必须要设置响应头才能正常工作。如果不设置响应头为 <code>Content-Type:application/xml</code> ,IE浏览器会在你访问XML元素时抛出 “Object Expected” 错误。</div>
<div class="note"><strong>Note 2</strong>: 如果不设置响应头 <code>Cache-Control: no-cache</code> 浏览器将会把响应缓存下来而且再也无法重新提交请求。你也可以添加一个总是不同的 GET 参数,比如时间戳或者随机数 (详情见 <a href="/en/DOM/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache">bypassing the cache</a>)</div>
<div class="note"><strong>Note 3</strong>: 如果变量 <code>httpRequest</code> 在全局范围内使用,它会在 <code>makeRequest()</code> 函数中被相互覆盖,从而导致资源竞争。为了避免这个情况,请在包含 AJAX 函数的<a href="/zh-CN/docs/Web/JavaScript/Closures">闭包</a>中声明 <code>httpRequest </code>变量。</div>
<p>在通信错误的事件中(例如服务器宕机),在访问响应状态 onreadystatechange 方法中会抛出一个例外。为了缓和这种情况,则可以使用 <code>try...catch</code> 把 <code>if...then</code> 语句包裹起来。</p>
<pre class="brush: js">function alertContents() {
try {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
alert(httpRequest.responseText);
} else {
alert('There was a problem with the request.');
}
}
}
catch( e ) {
alert('Caught Exception: ' + e.description);
}
}
</pre>
<h2 id="Step_4_–_处理_XML_响应">Step 4 – 处理 XML 响应</h2>
<p>在上一个例子中,在收到HTTP请求的响应后我们会请求对象的 <code>responseText</code> 属性,包含 <code>test.html</code> 文件的内容。现在我们试试 <code>responseXML </code>属性。 </p>
<p>首先,我们创建一个稍后将要请求的有效的 XML 文档。文档(<code>test.html</code>)包含以下内容:</p>
<pre class="brush: html"><?xml version="1.0" ?>
<root>
I'm a test.
</root>
</pre>
<p>在脚本里我们只需要把请求行改为:</p>
<pre class="brush: html">...
onclick="makeRequest('test.xml')">
...
</pre>
<p>然后在 <code>alertContents()</code> 里,我们把 <code>alert(httpRequest.responseText)</code> 改为:</p>
<pre class="brush: js">var xmldoc = httpRequest.responseXML;
var root_node = xmldoc.getElementsByTagName('root').item(0);
alert(root_node.firstChild.data);
</pre>
<p>这部分代码采用 <code>responseXML</code> 提供的 <code>XMLDocument</code> 对象,并使用 DOM 方法访问 XML 文档中包含的一些数据。你可以在<a href="http://www.w3clubs.com/mozdev/test.xml">这里</a>查看 <code>test.xml</code> 并且在<a href="http://www.w3clubs.com/mozdev/httprequest_test_xml.html">这里</a>更新测试代码。</p>
<h2 id="Step_5_–_处理数据">Step 5 – 处理数据</h2>
<p>最后,我们发送一个数据给服务器并收到响应。这次我们用 JavaScript 请求动态页面,<code>test.php</code> 并返回一个计算后的字符串 - “Hello, [user date]”,并用 <code>alert()</code> 出来。</p>
<p>首先要添加一个文本到 HTML 中以方便用户输入名字:</p>
<pre class="brush: html"><label>Your name:
<input type="text" id="ajaxTextbox" />
</label>
<span id="ajaxButton" style="cursor: pointer; text-decoration: underline">
Make a request
</span>
</pre>
<p>还要添加事件处理程序,从表单中获取用户数据连同服务器端的UTL一并发送给 makeRequest() 函数:</p>
<pre class="brush: js"> document.getElementById("ajaxButton").onclick = function() {
var userName = document.getElementById("ajaxTextbox").value;
makeRequest('test.php',userName);
};
</pre>
<p>我们还要修改 <code>makeRequest()</code> 让它接受用户数据并将其发给服务器。把请求方法从 <code>GET</code> 改为 <code>POST</code>,把数据作为参数让<code> httpRequest.send()</code> 调用。</p>
<pre class="brush: js"> function makeRequest(url, userName) {
...
httpRequest.onreadystatechange = alertContents;
httpRequest.open('POST', url);
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send('userName=' + encodeURIComponent(userName));
}
</pre>
<p>如果这就是服务器返回的全部内容,<code>alertContents()</code> 函数可以使用 step 3 中的相同函数写。可是,服务器会返回计算后的内容和原内容。所以,如果用户输入 “Jane” 在输入框中,那服务器就会返回如下内容:</p>
<p><code>{"userData":"Jane","computedString":"Hi, Jane!"}</code></p>
<p>为了在 <code>alertContents()</code> 中使用这个数据,我们可不能只是alert <code>responseText</code> ,我们要解析它并 alert <code>computedString</code>,我们想要的属性:</p>
<pre class="brush: js">function alertContents() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
var response = JSON.parse(httpRequest.responseText);
alert(response.computedString);
} else {
alert('There was a problem with the request.');
}
}
}
</pre>
<p><code>test.php 文件应该包含以下内容:</code></p>
<pre class="brush: php"><code>$name = (isset($_POST['userName'])) ? $_POST['userName'] : 'no name';
$computedString = "Hi, " . $name;
$array = ['userName' => $name, 'computedString' => $computedString];
echo json_encode($array);</code></pre>
<p><font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: #eeeeee;">想获取更多 DOM 方法,可以查看</span></font><code> <a class="external" href="http://www.mozilla.org/docs/dom/">Mozilla's DOM implementation</a> 文档.</code></p>
|