图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

4.3  jQuery

jQuery,自称为“新式”JavaScript库,相比我们在本章接触过的其他工具包,它的编程理念稍有不同。jQuery力图改变JavaScript的编程方式,并且jQuery的哲学确实对我们开发脚本的方式产生了巨大的冲击。

http://jquery.com/上可以找到jQuery的下载以及文档。你可以下载未经压缩的程序库(代码可供阅读),也可以下载一个较小的经过压缩的文件(不具可读性)。

不管是哪个,在用到jQuery的页面中,都需要导入jQuery脚本文件。根据本节的目的,我们将使用没有压缩过的(可读的)版本,把它放在和示例页面相同文件夹下(以便于导入),并命名为jquery.js。

4.3.1  jQuery基础

在深入研究如何用jQuery发送Ajax请求之前,让我们先来看看一些基本概念。要理解jQuery的工作方式,首先就需要掌握这些概念。

本节并非是jQuery的完整教程——这需要多得多的篇幅——但是本节应该能让你对jQuery工作方式背后的哲学有个大致概念。

1.jQuery包装器

我们看到过的其他程序库,比如Prototype,其工作方式是通过引入新的类以及扩展内建的JavaScript类来增加我们页面上脚本的功能。例如在第3章中我们可以看到Prototype是如何扩展Object、Function以及Array类的。

jQuery则采取不同的方式。

jQuery不是对类进行扩展,而是提供了一个新类,其名字就是jQuery,它作为其他对象的包装器,为那些对象提供扩展操作。包装器对象的概念对于面向对象程序的资深开发者来说并不陌生。这一模式常用于适配器,它提供了一个与对象原本接口不同的接口来操作对象。

jQuery中,大多数操作是这样进行的:用jQuery包装器包装一组元素,然后调用包装器的方法对经过包装的元素进行操作。为了让包含jQuery包装器的表达式和语句更加紧凑,jQuery类被映射为$。不要把它和Prototype$()混为一谈,因为它的目的完全不同。

jQuery对象可以包装许多不同的对象类型,并且包装的对象不同,它所提供的功能也各有不同。例如,我们可以包装一段HTML:

这由HTML构建了一个DOM片段,我们可以使用jQuery的方法来操作它。例如,如果我们想要把这个片段加到文档的末尾,可以这样:

Ajax开发者经常需要生成新的DOM元素,采用这样便捷简短的方式就可以实现添加元素的功能,jQuery的优点是显而易见的。

除了添加新的DOM元素外,我们还经常需要操作页面中已有的元素。jQuery包装器也可以包装已有的元素,只需传给$()一个字符串,$()包装器支持许多种方式来标识出需要包装的项目:CSS选择器、XPath表达式以及元素名。我们将在示例代码中大量使用CSS选择器。比如:

这将对文档中的所有<div>元素进行包装以便操作。另一个示例是:

这会对idsomeId的DOM元素进行包装以便操作。还有第三个示例:

这将包装所有具有someClass类名的元素。

jQuery的作者非常聪明地运用了CSS选择器和XPath来标识目标元素,而不是发明某些jQuery专用语法去强迫用户接受。作为网页开发者,我们对这些机制已经相当熟悉,这让我们可以更容易接受和使用jQuery来标识所需操作的元素。

还可以包装其他项目,如元素和函数。稍后我们将看到这样的例子。

2.串连jQuery操作

jQuery还很明智地允许我们在一个单独的表达式中把许多操作串连起来。绝大多数jQuery包装器方法会返回一个jQuery包装器对象自身的引用,这样当我们需要对所包装的对象执行多个操作时,就能够在一个单独的表达式上不断追加操作。

考虑这种情况,我们想要给一个元素(它的idsomething)增加一个CSS类,并且将这个元素显示出来(假设最初它是隐藏的)。我们不用这样:

而可以这样写:

3.在文档就绪时执行代码

在页面中,我们往往需要执行一些初始化代码,以便在用户与应用交互前先做好准备工作。通常我们使用窗口对象的onload事件处理器来做初始化。它保证在执行onload代码前页面已加载完毕,由此保证了DOM元素已经存在,从而可以进行DOM操作了。

但是onload有一个问题,就是它不仅会等待文档主体加载完毕,还会等待图像等资源加载完毕。如果这些图像不在浏览器的缓存中,就必须从服务器上去拉回来,这样初始化代码运行的那个时间点就远远迟于文档本身加载完成的那个时间点,而当时已经完全可以执行初始化代码了。

jQuery帮我们解决了这一问题,它引入了“文档就绪处理器”的概念。这一机制令函数在文档加载完成之时执行,而不必等待图片及onload事件处理器。

要使用这一机制,我们需要对文档元素进行包装,并调用包装过的文档对象上的ready()方法:

一旦DOM就绪并可供操作,传给ready()的函数就会执行。注意,当你同时使用ready机制和页面上的onload事件处理器时,两者都会执行,且ready事件处理器会先于onload事件处理器被触发。

ready()处理器有一个简略记法,即直接用jQuery的包装器对一个函数进行包装。下面的代码片段:

与之前那段声明ready()处理器的代码是等价的。

4.同时使用jQuery和Prototype

Prototype非常流行,而jQuery也迅速走红。因此,网页创作者完全有可能想在一个页面中同时使用这两个库。

总的来说,jQuery遵循着最佳实践的准则,避免污染全局命名空间(global namespace)——例如将工具函数这类构造放到jQuery命名空间中而不是直接放在全局命名空间中。不过有一个地方存在冲突,就是我们之前提到过的,使用了$这一全局名称。

jQuery,作为JavaScript程序库世界中的一个模范公民,已经预先考虑到了这个问题。当在一个页面中同时使用Prototype和jQuery时,只要在两个程序库加载完成后调用jQuery的jQuery.noConflict()工具函数,就会使得$名字的功能恢复成Prototype的定义。

通过jQuery命名空间,你仍可以使用jQuery的功能,你也可以定义简短的别名。对于将jQuery与Prototype结合使用的情况,jQuery文档建议使用如下的别名:

好了,我们已经学习了足够的预备知识了!

在本节后续的解决方案中我们将看到jQuery的更多用途。即便如此,我们也只是稍微触及到了jQuery的皮毛。如果你在学习这些解决方案后发现自己已被jQuery的能力所深深吸引,那么我们强烈建议你访问http://docs.jquery.com/,阅读全面的在线文档并了解jQuery所提供的其他能力。

4.3.2  用jQuery进行异步加载

jQuery提供了不少方法来发送Ajax请求。有些是简单且实用的高层方法,用于初始化Ajax请求,并执行大多数一般的请求任务。其他一些方法是底层的,它们提供了对Ajax请求各方面的控制。

在本节的解决方案中,我们将使用具有代表性的一些方法。首先来处理最普遍的Ajax交互:从服务器获得动态内容。

1.问题

让我们来想象一个电子冰箱——一个假想的高科技电冰箱,它不仅可以保存食品,而且还提供一个网络接口能够让软件用来与其通信和交互。

电子冰箱用来记录其存货的虚拟技术并不重要。它可以是条形码扫描,RFID(无线电频率识别)标签,或者是其他虚构的技术。作为网页的作者,关心的是拥有一个服务器组件,能够让我们发出请求来获得食物状态的信息。

网页的焦点将是呈现电子冰箱中的食物列表。通过点击列表中的某一个食物,可以显示出更加详细的信息。

针对这个问题,我们将假设网页已经预先载入了食物的列表,它是通过某一种服务器端模板机制生成的。在下一节中,我们将看到一种从服务器动态获取列表的技术。

2.解决方案

一开始,为了在页面中使用jQuery,就必须导入jQuery库:

假设已经由服务器端机制生成了电子冰箱内的食物,并在一个select元素中陈列出来:

为了达到本示例的目的,我们仅列出8个食物。一般的冰箱通常会包含更多的食物,但是,我们都知道速食主义者的冰箱内的食物总是很稀少的。

服务器(或许可以叫做“电子冰箱驱动”)分配给食物一个标识号,它被用来唯一标识每个食物。在这里,简单地用一个连续整型值。这个标识值将被设置为每个<options>value,用来标明食物。

虽然我们知道需要select控件对用户的输入做出反应,但可以注意到,在创建<select>元素标记中并没有声明任何处理函数。这就需要提出jQuery设计背后的另一个体系。

jQuery的一个目标是让网页作者能够轻松地把脚本从文档标记中分离出来,这和CSS允许我们把表现从文档标记中分离出来的方法很像。诚然,我们可以不用jQuery来做到这些事,毕竟jQuery是用JavaScript编写的,而且不是所有无法做到的事它都去帮我们做了。但是,它还是为我们做了不少事,并且以轻松地从文档标记中分离脚本为目标来设计。所以,与其直接在<select>元素标记上增加一个onchange事件处理,还不如用借助jQuery的帮助,在脚本控制下增加这个事件处理。

在文档就绪前,我们无法操作页面上的DOM元素,所以在页面首部的<script>元素中,我们将用一个之前讨论过的jQuery ready()处理函数。在这个处理函数内,我们将用jQuery的方法来给元素增加一个change事件处理。请看下面的代码片段:

ready()处理函数内,我们通过传递itemsControl这个id创建了一个封装了<select>元素的jQuery实例。之后,用了jQuery的change()方法,它把自己的参数分配给封装的元素,作为它的change事件处理。

这里,我们已经声明了一个showItemInfo()函数。其中,我们将为列表中选中的食物创建Ajax请求。

获得食物id并把它

作为参数传递

 

给出服务器端资源

 

封装元素,并调用load方法

 

jQuery提供大量不同的方法来向服务器发送Ajax请求。这个解决方案的目的是希望从服务器得到一个预先格式化好的HTML片段(包含了食物的数据)。并且把它加载到一个元素中,即一个id是itemData<div>元素。jQuery的load()Œ方法可以完美地达到这个要求。

这个方法将从由第一个参数提供的URL处取得一个响应,并且把它插入到封装的DOM元素内。这个函数的第二个参数允许我们传递一个对象,它的属性将作为请求的参数。第三个参数可以用来指定一个在请求完成后执行的回调函数。

首先,我们封装一个DOM元素Œ,它通过CSS选择器div#itemData来定位,是一个用于把加载的食物数据放入其中的空<div>元素。之后,用load()方法,提供一个JSP页面的URL,它将获取由itemId请求参数指定的食物数据,这个参数是方法的第二个参数提供的Ž

那个参数的值必须是用于在<select>元素中用户点击的那个option的值。因为<select>元素被设置为chanage句柄的函数上下文,所以可以通过this引用来访问它。我们封装这个引用,并用jQueryval()方法来获得当前被选中的控件的值Ž

因为我想要做的就只是给DOM中加载食物数据,所以我们并不需要一个回调函数,省略了load()方法的第三个参数。

这就是全部了。

jQuery拥有许多常用的过程,它们实现了大量的代码,并且允许我们用很少的几行代码来执行它们。这个处理函数调用的JSP页面用了itemId请求参数的值来获取相应食物的信息,并把它格式成HTML显示在页面上。

图4-5展示了在选择电冰箱内的食物后显示的完成页面。其源代码在清单4-6中完整地列了出来。

4-5  要牛奶吗

代码清单4-6  用jQuery来决定晚饭吃什么

3.讨论

本节为我们介绍了jQuery执行Ajax请求的方法,即load()方法。

jQueryload()方法,非常适合用来让诸如JSP和PHP这种服务器端的模板语言格式化一小段数据,并把HTML作为响应返回。fetchItemData.jsp文件,以及仿造电子冰箱功能的Java类,可以在本章供下载的源代码中找到。

其他一些重要的jQuery特性,也在这个解决方案中表露出来。例如,我们用了一个ready()句柄来触发代码的执行,这些代码必须在用户能够和页面交互前但在整个DOM构建完成后执行。

我们还看到了val()方法,它返回一个封装的input元素的值。如果有多于一个元素被封装,那么这个方法将返回第一个匹配的元素的值。

在这个解决方案中,我们假设电子冰箱的原始列表是由任何一种服务器端语言通过生成页面而给出,比如,一个JSP模板。这是Web应用的一种普遍做法,但是,为了探索jQuery的Ajax能力,在下一个问题中,就需要在页面加载中动态获取那个列表。

4.3.3  用jQuery获取动态数据

在上一节中,我们介绍了jQuery的load()方法,它可以非常轻松地执行获取HTML片段并把它加载到一个DOM元素中的任务。虽然这个方法非常好用,但是有时我们却想要获得对Ajax请求过程的更多控制权,或者想从服务器获得数据(而不是预格式化好的HTML)。

在本节中,我们将探索jQuery在Ajax领域提供的更多的东西。

1.问题

我们希望扩展之前的代码,使它从页面初始化的异步请求中获得电子冰箱内的食物列表。

2.解决方案

回顾下上一个解决方案,可以发现为了动态地加载选择框的选项,我们需要做的修改就是从<select>元素中移除<options>元素,并且在ready()句柄中增加代码来获取并加载这些食物。但是,在着手之前,我们先来修改编写showItemInfo()处理函数的方式,这样可以来找个借口继续探索jQuery的性能。除了使用jQuery封装器的load()的方法,这次我们将使用jQuery的另外一个有用的函数:$.get();

不过,先等一下。那个函数里面的句点在这里是干嘛用的?这不像是我们一直在用的$()封装器。

事实上,jQuery不仅仅提供了我们一直用着很不错的那个封装器类,而且还提供了大量实用的方法,许多是作为$封装器类的类方法实现的。

假如$.functionName()这样的符号看上去很怪,那么可以想象不使用$别名的表达式:

好,这样看起来就比较熟悉了。$.get()函数是作为一个类方法定义的,也就是jQuery封装器函数的一个函数属性(如果类方法的概念还是让你很头痛,那么最好能回顾一下3.1.3节)。

虽然,我们知道了它们是作为jQuery封装器类的类方法,但是jQuery却把这些方法定义为实用的函数,并且还让它们同jQuery的术语保持一致。这就是我们将要在本节中应用它们的方式。

$.get()函数接受的参数和load()方法一样:请求的URL,一个请求参数的hash,以及一个在请求完成后执行的回调函数。在使用这个函数时,因为并没有封装的对象来自动地把响应填入其中,所以最好是明确指定可选参数的回调函数。这是在请求完成后处理事务的主要手段。

还必须注意的是,如果没有传递请求参数,还可以指定回调函数作为函数的第二个参数。

在内部,jQuery用了一些JavaScript的技巧来保证取得的是正确参数。

下面是用这个实用的函数重写的showItemInfo()

除了使用$.get()函数外,对之前的代码所做的另一个修改是增加了一个回调函数作为第三个参数。我们可以用它来把返回的HTML插入到itemData元素中。

于是,我们用另外两个封装器方法:empty(),它用来清除封装的DOM元素;append(),它用来给封装的元素增加用data参数传递给回调函数的HTML片段。

现在,我们已经准备好要在文档加载后给<options>填入从服务器获得的数据。这里,我们将采用JavaScript hash对象的格式,从服务器获得原始数据。虽然我们可以返回XML数据,但是为了使JavaScript代码便于处理,将采用JSON。

jQuery再次用一个实用的函数帮了我们的大忙,它非常适合这次的任务:$.getJSON()。这个函数接受已经非常熟悉的三个参数:一个URL,一个请求参数的hash,以及一个回调函数。

$.getJSON()所带来的优势是,回调函数调用时将获得已经执行过的JSON结构体。我们不再需要去对返回的响应做任何的执行。多么方便啊!

用这个实用的方法,需要在文档的ready()句柄中加入下面这一行:

一个fetchItemList.jsp的JSP页面被作为URL,并且提供了loadItems()(接下去会看到它的定义)函数作为回调。注意,因为我们不需要传递任何请求参数,所以就可以简单地忽略对象hash,并提供回调函数,把它作为第二个参数。

LoadItems()函数定义如下:

定位Select元素

 

增加新的option

 

调用回调函数时使用

执行过的JSON结构

 

回忆一下,$.getJSON()函数在调用回调函数时,将传递已经执行过的JSON响应Œ。在这个解决方案中,fetchItemList.jsp返回的响应包括:

当调用回调函数时,请求字符串已经被转换成一个JavaScript对象数组,其中每个对象都包含了一个id和一个name属性。每个对象还将被用来构造一个新的<options>元素,并用一种熟悉的方式把它加入到select控件中,这在4.2.1节的解决方案中已经出现过。

为了给<select>元素增加选项,我们需要这个控件DOM元素的引用。我们可以用document. getElementById()$(),但是,我们还是选择了jQuery的方式,即get()封装器方法:

当没有传递任何参数时,这个方法将返回所有匹配CSS选择器的元素数组。如果,只想要其中的一个,那么就可以指定一个从0开始的索引作为参数。这里,因为使用了一个id,所以就知道只有一个单独匹配符合这个选择器,因此,就指定0号索引来返回第一个匹配的元素。

代码清单4-7列出了整个页面的代码,其中对上个解决方案进行修改的地方够用高亮显示了出来。

代码清单4-7  用jQuery来获得更多晚餐的食物

3.讨论

这一节,让我们领略了jQuery在DOM操作和遍历以及Ajax请求领域上更多出色的性能。我们了解了jQuery$.get()函数,它使得我们能够轻松地用HTTP GET方法来发送Ajax请求。另一个有着同样函数签名的$.post()通信函数,可以同样轻松地通过Ajax提交POST请求。因为两个函数都使用同样的参数签名——清晰的请求参数hash。这样,就可以简单地在我们喜欢的任何一种HTTP方法之间进行切换,不必为请求参数应该如何编码的细节而烦恼。

另外一个Ajax实用函数$.getJSON()可以让我们极其轻松地使用服务器端格式化并返回的JSON符号。在调用这个操作的回调函数时,JSON字符串已经被执行过了,这就让我们能够从处理JavaScript eval()函数的不可知行为中解脱出来。

在我们希望能够获得更多控制权且能够清楚Ajax请求的情况下,jQuery提供了一个叫做$.ajax()的通用实用函数。在线文档对如何使用底层的实用函数提供了更多细节。

我们还看到了一些强大的DOM操作封装器方法,比如get()empty(),val()以及append(),这些都让作为Ajax网页开发者的我们能够轻松地操作页面的DOM。

在这里,几乎不可能完整展现jQuery性能的深度。例如,比如无法探索effects的API,它提供了fading、sliding、flashing、hovering等众多特效。甚至拥有自己提供动画的能力。我们鼓励你访问http://jquery.com/来获得jQuery的更多信息,以及获知它是如何帮助编写强大的Ajax应用的。

此外,高级的开发者可能会对jQuery的插件程序API感兴趣。这个API是jQuery众多强大性能中的一个,因为它可以让任何人都可以立刻扩展这个工具集。更多信息请详见http://docs.jquery. com/Plugins/Authoring

现在,有些事物已经完全不同了。让我们开始研究第四个框架,它将用一种全新的角度来处理异步请求的问题。