在第 6 章中,我们学习了如何让 jQuery 帮助我们为用户制作更好的表单。填写表单后,我们需要使用 jQuery 将其发送回服务器并获取新数据。我们生活在单页、流畅的应用程序世界中。互联网上的大多数顶级网站都会通过 Ajax 无缝地更新页面中需要更改的部分。对于用户来说,这比通过提交按钮发布数据并加载新页面的老式方式更好。jQuery 已经准备好帮助我们了。我们可以使用它从服务器上按需获取新数据。
在本章中,我们将介绍以下主题:
- jQuery 之前的生活
- jQuery 如何帮助我们今天
- 辅助方法
- Ajax 事件
为了理解 jQuery 如何帮助我们与服务器对话,我们应该先退一步,探索 jQuery 之前的生活。在此期间,网站有两种方式向服务器发送数据:<form>
和<a>
标签。
HTML<form>
标记是向服务器发送数据的元素。它有两个属性处理如何将数据发送到服务器。首先,有method
属性,它可以指定如何将数据发送回服务器。它有两个可能的值:get
或post
。
将method
属性设置为get
,并将追加到请求末尾的表单数据发送到action
属性指定的服务器页面。表单将从定义了name
元素的所有启用表单元素发送数据。get
方法只能用于少量不敏感数据。通过get
发送的所有数据都可以从浏览器的 URL 栏中看到。
将 method 属性设置为post
被认为比get
更安全,因为它在消息体中发送数据,所以在查询字符串中不可见;但不要被愚弄到认为数据是安全的,它只是不可见的。当您向服务器发送新数据时,post
应该是您的 go-to 方法。
<form>
标记包含所有表单数据元素,当单击提交按钮时,这些表单数据元素将被发送到服务器。请记住,只发送有效的元素。为了有效,元素必须启用并具有name
属性。name
属性是将在服务器上给出的值的名称。与id
属性不同,name
值可以复制,但如果它们在表单中复制,则由您确定哪个是哪个。
另一种有时被忽略的向服务器发送少量信息的方法是设置<a>
标记的href
属性的查询参数。诚然,它只能发送小块信息,但当您需要将数据从一个页面发送到下一个页面时,它确实非常有用。
<form>
和<a>
标记都会导致页面刷新,但在 jQuery 之前可以使用 Ajax。自 20 世纪 90 年代末以来,很多人都没有意识到 Ajax 在微软浏览器中已经成为可能。它们是使用微软专有的 ActiveX 对象实现的,但这一功能的有用性并没有被其他浏览器制造商所失去,他们将其作为浏览器的对象,简称为XMLHTTPRequest
对象或 XHR。
不幸的是,编写支持此功能的代码并不容易。就像我们过去在浏览器编程中看到的许多事情一样,类似函数的不同实现导致我们开发人员在开始编写功能代码之前必须编写大量管道代码。让我们看看 jQuery 为 Ajax 聚会带来了什么。
jQuery 帮助我们的方法之一是减轻 Ajax 的痛苦。用户不再希望在单击提交、页面变为空白然后加载新内容的循环中等待。Facebook、Gmail 和 Twitter 等网站向用户展示了 web 可以非常像应用程序。尽管 jQuery 是一个库,而不是像 AngularJS 或 Ember 那样的编程框架,但它可以轻松地获取和发送服务器数据,而无需重新加载页面。
为了演示本章中的代码片段,您必须设置一个 web 服务器。但是,设置 web 服务器超出了本书的范围。一种简单的方法是使用包含内置 web 服务器的编辑器/IDE。两个这样的编辑器是 JetBrains WebStorm 和 Adobe 的括号。两者都适用于 Windows、Mac OS X 和 Linux。
我们希望我们的网站能够做的第一件事就是在页面上加载新的 HTML 标记。这是的位置。load()
方法派上了用场。它使用 Ajax 从服务器上的 URL 下载 HTML,并将其插入指定位置的页面。如果你需要创建一个简单的单页应用程序,这个方法会使它变得简单。在封面下。load()
使用HTTP GET
方法,这与浏览器加载 HTML 时使用的方法相同。让我们看看一些代码:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<title>Chapter07-AJAX</title>
<style type="text/css">
.output-region {
border: 2px dashed lightblue;
width: 100%;
height: 200px;
}
</style>
</head>
<body>
<div>
<div class="output-region" id="outputRegion"></div>
<form id="myForm">
<select name="greeting">
<option selected value="Mr.">Mr.</option>
<option value="Mrs.">Mrs.</option>
<option value="Ms.">Ms.</option>
<option value="Miss">Miss.</option>
<option value="Dr.">Dr.</option>
</select>
<input name="firstName" value="Abel"/>
<!-- is disabled -->
<input name="middleName" disabled value="Middle"/>
<input name="lastName" value="Alpha"/>
<!-- doesn't have a name attribute -->
<input id="suffix" value="Suffix"/>
<input name="age" value="42"/>
</form>
<div id="dataTransfer" style="display: none;"><hr/>Data transfer in progress...</div>
<hr/>
<button type="button" class="load-page" data-page="page1">Get Page 1</button>
<button type="button" class="load-page" data-page="page2">Get Page 2</button>
<button type="button" class="load-page" data-page="page3">Get Page 3</button>
<button type="button" id="get-javascript">Get JavaScript</button>
<button type="button" id="get-json">Get JSON</button>
<button type="button" id="get-get">Get Get Data</button>
<button type="button" id="get-post">Get Post Data</button>
<button type="button" id="jq-param">Create Param</button>
<button type="button" id="serialize">Serialize</button>
<button type="button" id="serializeArray">Serialize Array</button>
</div>
<script type="text/javascript">
(function (window, $, undefined) {
"use strict";
// Hook the document ready event and
$(document).ready(function () {
// error display
function showError(err) {
alert('ERROR: ' + err.status + ' - ' + err.statusText);
}
function showJsonMessage(data, erase) {
if(erase){
$('#outputRegion').text("");
}
$('#outputRegion').append($("<div>").text(JSON.stringify(data)));
}
// load new HTML markup code
$('.load-page').click(function (event) {
var fileToLoad = $(this).attr("data-page") + ".html";
$('#outputRegion').load(fileToLoad);
});
});
}());
</script>
</body>
</html>
前面的代码分为三部分。第一部分是<head>
标签中的所有内容。这里唯一重要的事情是,我们从在线 repo 加载 jQuery,并包含一些内联 CSS,以描述我们最终将在何处注入标记和 JSON 数据。
下一节是我们的 HTML 标记。我们有一个很大的<div>
和id
的输出区域,它将保存我们代码的结果。在<form>
标记中,有几个表单元素将为表单提供一些数据。HTML 的最后一行是一系列按钮,它们将激活我们的每个代码片段。
文件的最后一部分是我们的 JavaScript。最初,我们只有一个函数,但随着本章的深入,我们将添加更多代码。让我们从检查加载页面单击事件处理程序开始。
我们已经看到了很多事件处理程序代码,这里没有什么新内容。单击前三个按钮时,每个按钮都使用此处理程序。处理程序将获得单击按钮的data-page
属性。data-page
属性告诉代码要加载哪个页面,并附加了.html
的扩展名。这将传递给。load()
方法,使用它从服务器获取新标记并将其写入选择器指定的位置。如果.load()
成功检索 HTML,它将写入指定的选择器,在本例中,该选择器是 ID 为outputRegion
的<div>
。
.getJSON()
方法从传递的 URL 加载 JSON 数据,并使用返回的数据调用成功函数,或者将错误对象传递给失败的函数。与 jQuery1.5 及更高版本中的大多数 Ajax 方法一样,它还返回 jQuery 承诺。
承诺是表示异步操作最终结果的对象。它可以有三种状态之一:挂起、完成和拒绝。当它第一次被创建且尚未解决时,它的状态为“挂起”。如果承诺成功解决,其状态将变为已履行。如果承诺失败,其状态将变为拒绝。一旦承诺的状态从挂起更改,它就再也不会更改。
有了 jQuery 承诺,我们将then
函数链接到$.getJSON
方法。then
函数采用两个参数。第一个是一个函数,如果承诺成功实现,则调用该函数。第二个参数是发生错误时要调用的函数。如果一切正常,JSON 数据将转换为 JavaScript 对象并传递给success
函数,并在警报提示中显示消息;否则将显示错误对象的内容。
// load JSON data
$('#get-json').click(function (event) {
$.getJSON('data.json').then(function(data){
alert(data.message);
}, function(err){
alert('ERROR:' + JSON.stringify(err));
});
}0029;
大多数 Ajax 方法都从服务器获取某种数据。.getScript()
方法不同。它从服务器检索 JavaScript,解析并执行它。与其他 Ajax 方法一样,它返回一个承诺,但在本例中,success
函数没有传递任何数据。
// load and run javascript
$('#get-javascript').click(function (event) {
$.getScript('script.js').then(function () {
alert("getScript() was successful.");
}, function(err){
alert('ERROR:' + JSON.stringify(err));
});
});
.getScript()
方法加载的代码在执行后仍然可用,但是没有简单的方法再次调用代码,除非您保留对它的引用。在示例代码中,我们将incrementer
函数分配给 window 对象,以便以后可以调用它。
// wrap the code in a function for information hiding
(function () {
"use strict"
// we show a message to the user
alert("We're from another script file, trust us");
// bind the function to the window global object so I can call it if I need it.
window.incrementer = (function () {
var lastNum = 0;
return function (num) {
lastNum += num;
return lastNum;
}
}());
}());
最后两种速记方法是$.get()
和$.post()
方法。我们将一起描述它们,因为这两种方法都是jQuery.ajax()
方法的捷径。记住任何使用快捷方式的操作都可以通过调用$.ajax()
来完成。快捷方式处理了大量进行ajax
调用的繁重工作。让我们查看一些代码:
// load JSON via $.ajax
$('#get-get').click(function (event) {
$.ajax({
method: "GET",url: "data1.json",
success: function (data) {
showJsonMessage(data);
});
});
此代码使用HTTP GET
方法加载数据。$.get()
快捷方式允许我们将其改写为:
// load JSON via $.get
$('#get-get').click(function (event) {
$.get("data1.json", function (data) {
showJsonMessage(data);
});
});
我们只传递了方法的两个参数:数据的 URL 和成功函数;我们甚至懒得传递错误函数处理程序。记住,如果没有错误处理程序,浏览器将默默地接受任何错误。前面的代码演示了如何使用带有回调函数的$.get()
方法。让我们通过演示如何使用承诺链接来使事情变得更有趣。
// load JSON via $.get with promise chaining
$('#get-get2').click(function (event) {
$.get("data1.json").
then(function (data) {
showJsonMessage(data, true);
return $.get("data2.json");
}).
then(function (data) {
showJsonMessage(data);
return $.get("data3.json");
}).
then(function (data) {
showJsonMessage(data);
return $.get("data4.json");
}).
then(function (data) {
showJsonMessage(data);
}, showError);
});
前面的代码对服务器进行了四次连续调用。每个调用请求一个不同的 JSON 数据位,并通过showJsonMethod()
函数呈现。如果任何一个调用失败,将调用最后一个then()
方法的showError()
函数,并且不会发出进一步的 Ajax 请求。每个成功的调用都会返回下一个调用,以便能够将承诺链接在一起。
一个潜在的缺点是通话是按顺序进行的。大多数浏览器至少可以同时执行两个 HTTP 请求,有些浏览器可以执行更多操作。如果性能是一个问题,而调用的顺序不是,我们可以同时发出所有 HTTP 请求,让浏览器确定可以处理多少。幸运的是,jQuery 承诺有一个$.when()
方法。它接受您希望作为参数等待的所有承诺。一旦所有承诺被解决或拒绝,就会调用.then()
方法。发送到每个承诺的数据作为参数发送,发送顺序与.when()
方法中列出的承诺相同。
// load JSON via $.get with concurrent promises
$('#get-con').click(function (event) {
var data1 = $.get("data1.json"),
data2 = $.get("data2.json"),
data3 = $.get("data3.json"),
data4 = $.get("data4.json");
$.when(data1, data2, data3, data4).then(function(d1, d2, d3, d4){
showJsonMessage(d1, true);
showJsonMessage(d2);
showJsonMessage(d3);
showJsonMessage(d4);
}, showError);
});
在前面的代码中,发出了相同的四个 HTTP 请求,但现在它们是同时发出的。每个调用返回数据的顺序是不确定的,但代码将等待所有四个调用完成后再继续。如果任何调用失败,则认为整个操作失败,并调用 failure 方法。这与我们顺序调用代码时不同。每个 HTTP 请求都可以返回数据,但一旦其中一个失败,就会调用 failure 方法,不再发出任何请求。
到目前为止,所有请求都是通过$.get()
方法提出的;我们完全忽略了$.post()
方法。不用担心,这两种方法都是$.ajax()
方法的捷径。我们几乎可以在任何地方使用$.post()
代替$.get()
方法。因此,让我们替换第一个演示中的$.get()
方法。
// load JSON via $.post
,/. $('#get-post').click(function (event) {
$.post("data1.json", function (data) {
showJsonMessage(data, true);
});
});
那么,如果这些方法可以互相交换,为什么两者都可以呢?公平地说,我们没有按照预期的方式使用它们。四个主要的 HTTP 动词是 get、post、put 和 delete。Get 旨在从数据库中检索一个或多个项目。Post 用于在数据库中创建新记录。Put 用于更新现有记录。最后,delete 从数据库中删除一条记录。正确使用这些动词是 RESTful 服务 API 的核心,这与本书的主题有点脱节。
jQuery 为我们提供了一些 Ajax 助手方法。只有三个函数,每个函数都很容易使用,并且消除了一些繁重的工作。
Ajax 请求通常以查询字符串的形式对要传递给服务器的数据进行编码。查询字符串跟随 URL 并以问号“?”开头。每个参数由一个名称和一个由等号分隔的值组成。生成参数数据并不困难,但是需要正确遵循一些编码规则,否则您的请求可能会失败。$.param()
将将我们的数据编码为查询字符串格式。
// converts an array of objects to an encoded query string
$('#jq-param').click(function (event) {
var testData = [
{name: "first", value: "Troy"},
{name: "last", value: "Miles"},
{name: "twitter", value: "@therockncoder"}
];
var myParam = $.param(testData);
$('#outputRegion').text(myParam);
});
在前面的代码中,我们对名为testData
的对象数组进行编码。每个对象都有一个名称和一个值属性。这些属性将是编码字符串的名称和值。我们将数组传递给$.param()
方法,并将结果字符串存储在myParam
中,然后将其写入输出<div>
。
注意方法如何为我们处理编码。第三个数组参数包含 at 符号@
,正确编码为%40
。它还将对其他符号和空间进行编码。必须对这些符号进行编码,否则 Ajax 请求将失败。或者更糟糕的是,它似乎可以工作,但发送和存储的数据不正确。
$.param()
方法仅在手动创建 URL 时才是必需的。如果您调用$.get()
或$.post()
方法并将数据传递给它们,它们将正确编码并附加到 URL 或消息正文中。
下一个助手方法serialize()
与$.param()
方法类似,只是它没有传递数据,而是使用选择器指示的<form>
标记从所有有效表单元素中提取数据。
$('#serialize').click(function (event) {
var myParam = $('#myForm').serialize();
$('#outputRegion').text(myParam);
});
前面的代码在单击 Serialize 按钮时序列化了myForm <form>
标记中的所有表单元素,并将它们呈现到页面中。请记住,只有有效的表单元素才会被序列化。如果一个元素被禁用或没有 name 属性,它将不会被序列化。此方法允许您用 jQuery 代替老式的 HTML 表单提交。
Ajax 助手的最后一个成员是.serializeArray()
方法。与前面描述的.serialize()
方法一样,它从选择器指定的<form>
标记中的所有表单元素获取其数据。它只使用有效的表单元素,这些元素必须启用并具有名称元素。这种方法与.serialize()
方法的区别在于数据的编码方式。.serializeArray()
方法将表单数据编码为 JavaScript 对象数组。每个对象由一个名称和一个值属性组成。名称是元素的 name 属性的内容,值是元素的值。我们可以用这个方法代替.serialize()
方法。
// serialze the form data as an array of objects
$('#serializeArray').click(function (event) {
var myParam = $('#myForm').serializeArray();
$('#outputRegion').text(JSON.stringify(myParam));
});
调用.serializeArray()
的结果是一个 JavaScript 对象数组。我们将结果放在变量myParam
中,该变量被发送到 JSONstringify()
方法,以便显示。
有时,您的应用程序希望知道各种 Ajax 事件何时发生。您的应用程序可能希望显示一个图标,指示正在向服务器发送数据或从服务器接收数据,并在请求完成后隐藏该图标。幸运的是,jQuery 为我们提供了全局 Ajax 事件。这些事件使我们能够知道任何 Ajax 活动何时开始、停止、发送数据、出错或成功。这些事件是全局的,因此它们必须钩住 document 元素。让我们将它们添加到当前的示例代码中。
var $doc = $(document);
// when an ajax request begins
$doc.ajaxStart(function () {
console.info("<<<< Triggered ajax start handler.");
$('#dataTransfer').show('fast');
});
// once all ajax request complete,
$doc.ajaxStop(function () {
console.info(">>>> Triggered ajax stop handler.");
$('#dataTransfer').hide('slow');
});
// called at the beginning of each request
$doc.ajaxSend(function (event, jqxhr, settings) {
console.info("#### Triggered ajaxSend handler for: " + settings.url);
});
// called every time a request succeeds
$doc.ajaxSuccess(function (event, jqxhr, settings) {
console.info("#### Triggered ajaxSuccess handler for: " + settings.url);
});
// called every time a request fails
$doc.ajaxError(function (event, jqxhr, settings) {
console.info("#### Triggered ajaxError handler for: " + settings.url);
});
// called at the end of every request whether it succeeds or fails
$doc.ajaxComplete(function (event, jqxhr, settings) {
console.info("#### Triggered ajaxComplete handler for: " + settings.url);
});
在示例代码中,我们钩住了三个 Ajax 事件:ajaxStart
、ajaxStop
和ajaxSend
。
当第一个 Ajax 请求开始时触发.ajaxStart()
方法。如果另一个请求已在进行中,则不会触发该事件。在示例代码中,我们使隐藏的<div>
在该事件的处理程序中可见消息数据传输正在进行…。
一旦所有 Ajax 请求完成,.ajaxStop()
就会触发。就像.ajaxStart()
方法一样,它足够聪明,只能在适当的时候开火,允许我们将它们配对以隐藏和显示消息。
在本地运行代码时,您会发现stop
事件在start
事件之后很快被触发。为了让消息被看到,我们向hide
方法发送了一个参数slow
。如果没有此参数,用户将很难读取消息。
在发送 Ajax 数据之前,调用.ajaxSend()
处理程序。如果需要区分哪个 Ajax 请求触发了.ajaxSend()
,它会向处理函数发送三个参数:event
、jqxhr
和settings
。设置参数是一个包含两个重要属性的对象:url
和type
。url
属性是一个字符串,用于保存请求调用的 URL。type
是请求使用的 HTTP 动词。通过检查这两个属性,您应该能够确定哪些 Ajax 请求触发了事件。
如果请求失败,将触发.ajaxError()
处理程序。jQuery XHR 对象参数jqxhr
将保存错误信息。状态代码将位于 status 属性中,错误消息将位于statusText
属性中。
如果请求成功,将触发.ajaxSuccess()
处理程序。jQuery XHR 对象将保存状态信息。同样,状态代码在status
属性中,状态文本在statusText
属性中。
只要 Ajax 请求完成,.ajaxComplete()
处理程序就会被调用。无论请求是成功还是失败,始终调用此方法。它总是在成功或错误事件之后调用。
单个请求调用事件的顺序始终相同。首先是开始事件,然后是发送,然后是成功或错误事件,然后是完成事件,最后是停止事件。如果发出了多个请求,则触发事件的顺序将变得不确定。唯一可以保证的是,开始是第一个事件,停止是最后一个事件。
现代网络应用程序必须能够顺利地向其服务器发送和检索数据。jQuery 可以帮助我们与服务器无缝交互,并且无需进行整页刷新。
在本章中,我们学习了如何从服务器中提取新数据、JavaScript 和 HTML。另外,如何在不刷新页面的情况下将数据发布到服务器。我们还了解了 jQuery 提供的一些帮助方法,这些方法使我们更容易正确地打包数据以进行传输。
对 jQuery 的一个主要批评不是库本身,而是使用它编写的应用程序往往很快变得难以控制。在下一章中,我们将研究如何避免代码看起来像意大利面条。