javascript的核心API设计的很简单,但由于浏览器之间的不兼容性,导致客户端的API过于复杂。IE9的到来缓解了这种情况。然而使用javascript的框架或者工具类库,能简化通用操作,处理浏览器之间的差异,这让很多程序员在开发web应用时变得更简单。
jQuery类库被广泛的使用,作为web开发者,我们必须熟悉它:即便没有在自己的代码中遇到,也可能在别人的代码中遇见。幸运的是,jQuery代码足够小巧和稳定,本章就能将它讲述清楚。
jQuery能让你在文档中轻松找到关心的元素,并对这些元素进行操作:添加内容、编辑HTML属性和css属性、定义事件处理程序,以及执行动画。它还拥有Ajax工具来动态发起HTTP请求,以及一些通用的工具函数来操作对象和数组。
正如其名,jQuery类库聚焦于查询。一个典型的查询使用CSS选择器来识别一组文档元素,并返回一个对象来表示这些元素。返回的对象提供了大量的方法来批量操作匹配的元素。这些方法会尽可能返回调用对象本身,这使得简洁的链式调用成为可能。jQuery如此强大和好用,关键得益于以下特性:
本章首先会介绍如何使用jQuery来实现 简单的查询并操作其结果。接下来的章节会讲解:
1.jQuery基础
jQuery类库定义了一个全局函数:jQuery()。该函数使用频繁,因此在 类库中还给它定义了一个别名:$,这是jQuery在全局命名空间中定义的唯一两个变量。(如果你在自己的代码中有使用$作为变量,或者引入了Prototype等使用$作为全局变量的类库,这时,为了避免冲突,可以使用jQuery.noConflict()来释放$变量,让其指向原始值。)
这个拥有两个名字的全局方法是jQuery的核心查询方法,例如下面的代码能获取jQuery文档中的所有<div>元素:
var divs = $("div")
改方法返回的值表达零个或多个DOM元素,这就是jQuery对象。注意:jQuery()是工厂函数,不是构造函数,它返回一个新创建的对象,但并没有和new关键字一起使用。jQuery对象定义了很多方法,可以用来操作它们表示这组元素,本章中的大部分文字来阐述这些方法。例如:下面的这组代码用来找到拥有details类的p元素,将其高亮显示,并将其中隐藏的p元素快速显示出来。
$("p.details").css("background-color","yellow").show("fast");
上面的css()方法调用操作的jQuery对象是由$()返回的,css()方法返回的这个也是这个对象,因此可以继续调用show()方法,这就是链式调用,很简洁紧凑。在jQuery编程中,链式调用这个习惯用语很普遍。再举个例子,下面的代码可以找到文档中拥有"clicktohide"CSS类的所有元素,并给每一个元素都注册一个事件处理函数。当用户单击元素时,会调用事件处理程序,使得元素缓慢向上收缩,最终消失。
$(".clicktohide").click(function(){$(this).slideUp("slow");});
i.jQuery()函数
在jQuery类库中,最重要的方法是jQuery()方法,(也就是$())。它的功能很强大,有四种不同的调用方式。
第一种也是最常用的调用方式是传递CSS选择器(字符串)给$()方法。当通过这种方式调用时,$()方法会返回当前文档中匹配该选择器的元素集。jQuery支持大部分css3选择器语法,还支持一些自己的扩展语法。8小节i节将阐述jQuery选择器的语法。还可以将一个元素或jQuery对象作为第二个参数传递给$()方法,这时返回的是该特定元素或元素的子元素中匹配选择器的部分。第二个参数是可选的,定义了元素的查询的起点,经常称为上下文(context)。
第二种调用方式是传递一个Element、Document或window对象给$()方法。在这种情况下,$()方法只须简单地将Element、Document或window对象封装成jQuery对象并返回。这样可以使得能用jQuery方法来操作这些元素而不使用原生的DOM方法。例如,在jQuery程序中,经常可以看见$(document)或$(this)。jQuery对象可以表示文档中多个元素,也可以传递一个元素数组给$()方法。在这种情况下,返回的jQuery对象表示该数组中的元素集。
第三种调用方式是传递HTML文本字符串给$()方法。这这种情况下,jQuery会根据传入的文本创建好HTML元素并封装为jQuery对象返回。jQuery不会将刚创建的元素自动插入文档中,可以使用第3节描述的jQuery方法将元素插入到想要的地方。注意,在这种调用下,不可传入纯文本,因为jQuery会把纯文本当做是CSS选择器来解析。当使用这种调运风格时,传递给$()字符串必须至少包含一个带尖括号的HTML标签。
通过第三种方式调用时,$()接受可选的第二参数。可以传递Document对象来指定与所创建关联的文档。(比如,当创建的元素要插入iframe里时,需要显式的指定iframe的对象。)第二参数还可以是object对象。此时。假设该对象的属性表示HTML属性的键/值对,这些属性将设置到所创建的对象上。当第二参数对象的属性名是css,html,text,width,heigeht,offset,val,data或者属性名是jQuery事件处理程序注册方法名时,jQuery将调用新创建元素上的同名方法,并传入属性值。(css(),html(),text()等方法将在第2节讲述,事件处理程序注册方法将在第4节讲述),例如:
var img = $("<img>", //新建一个<img>元素 { src: url, //具有html属性 css: {borderWidth: 5}, //css样式 click: handClick //和事件处理程序 });
第四种调用方式是传入一个函数给$()方法。此时,当文档加载完且DOM程序可以操作时,传入函数将被调用。这是常见onLoad()函数的jQuery版本。在jQuery程序中,在jQuery()里定义一个匿名函数非常常见:
jQuery(function(){//文档加载完毕时调用 //所有的代码放在这里 });
有时候我们还可以看见老式的写法$(f)和完整的写法
$(document).ready(f)
传递给jQuery()的函数在被调用时,this指向document对象,唯一的的参数指向jQuery函数 。这意味这可以释放全局的$()函数,但在内部依旧可以延续该习惯:
jQuery.noConflict();//还原$()为初始值 jQuery(function($){ //让$()成为jQuery对象的局部别名 //jQuery代码 });
通过$注册的函数将在DOMContentLoaded事件触发时由jQuery触发。当浏览器不支持该事件时,会在load事件触发时由jquery触发。这意味这文档已经完毕,但图片等外部资源还有可能未加载完。如果DOM准备就绪后再传递函数给$(),传递的函数会在$()返回之前立刻调用。
jQuery类库还使用jQuery()函数作为其命名空间,在下面定义了不少的工具函数和属性。比如jQuery.noConflict();就是其中的一个工具函数。还包括遍历的jQuery.each(),以及用来解析JSON文本的jQuery.parseJSON()。第7节例举了这些通用函数,jQuery的其它函数在本章中都会提及。
jQuery术语
在本章中会提到一些重要的术语和短语,我们来看下其定义
jQuery对象:
jQuery函数是jQuery或$()的值。该函数可以用来创建jQuery对象,用来注册DOM就绪时需要调用的处理程序,还用做jQuery命名空间。我们常用$()来引用它。它可以用来做命名空间,因此,jQuery函数也可称为"全局jQuery对象",但要千万注意,不要把它和"jQuery对象"混淆。
jQuery对象
jQuery对象是由jQuery函数返回的对象。一个jQuery对象表示一组文档元素,也叫“jQuery结果”,“jQuery集”或“包装集”。
jQuery函数
jQuery函数是指定义在jQuery命名空间中的函数,比如jQuery.noConflict()。jQuery函数也称为“静态方法”。
jQuery方法
jQuery方法是由jQuery函数返回的jQuery对象的方法。jQuery类库最重要的部分就是它定义的这些强大的方法,
jQuery函数和jQuery方法有时很难区分,因此有部分函数和方法的名称是一样的:注意以下两行代码的差异:
//jQuery的each()函数用来对数组a中的每一个元素都调用一次函数f $.each(a,f); //调用jQuery()函数获取表示文档中所有<a>元素的jQuery对象 //然后调用该jQuery对象的each()方法,对选中的每一个元素调用一次函数f $("a").each(f)
ii.查询与查询结果
传递css选择器字符串给$(),它返回的jQuery对象表示匹配(或称为“选中”)的元素集。css选择器在13.2.iiiii介绍过
$()返回值是一个jQuery对象,jQuery对象是类数组,它们拥有length属性和介于0~length-1之间的数值属性。(7章11节有更多类数组对象的信息),这意味着可以使用标准的数组标识方括号来访问jQuery对象的内容。
$("body").length //=>1 文档只要一个唯一的body元素 $("body")[0] //等于document.body
如果不想把数组标识用来jQuery对象上,可以使用size()方法来替代length属性,用get()方法来代替方括号索引。可以使用toArray方法来讲jQuery对象转化为真实数组。
除了length属性,jQuery对象还有三个挺有趣的属性。selector属性是创建jQuery对象时选择器字符串(如果有的话)、context属性是上下文对象,是传递给$()方法第二参数,如果没有,默认是Document对象。最后,所有jQuery对象都在一个名为jquery的属性,检测改属性是否存在可以简单便捷地将jQuery对象与其他类数组对象区分开来。jQuery属性值是字符串形式的jQuery版本号
//获取document body中的所有<script>元素 var bodyscript = $("script",document.body); bodyscript.selector;//=>script bodyscript.context; //=>document.body bodyscript.jquery;//=> "1.8.3"
$()与querySelectorAll()
$()函数与13章2节iiiii节描述的Document对象的querySelectorAll()方法类似:两者都有css选择器作为参数,并且返回类数组对象来存放匹配选择器的元素。在支持querySelectorAll()的浏览器中,jQuery实现调用会调用querySelectorAll()方法,然而,在代码中使用$()代替querySelectorAll()依旧是很好的选择:
想要遍历jQuery对象中的所有元素时,可以调用each()方法来代替for循环,each()方法有点类似ECMAScript5(ES5)中的forEach()数组方法。它接受一个回调函数作为唯一的参数,然后对jQuery对象的中每一个元素(按照文档中的顺序)调用回调函数。回调函数作为匹配元素的方法来调用,因此,在回调函数里this关键字指代Element对象。
each()方法还会将索引值和该元素作为第一个和第二个参数传递给回调函数。注意:this和第二参数都是原生文档元素,而不是jQuery对象;如果想使用jQuery方法来操作该元素,需要先用$()封装它。
jQuery的each()方法和forEach()有一个显著的区别:如果回调函数在任一个元素上返回false,遍历将在该元素中止(就像在普通循环中使用break关键字一样)。each()返回调用自身的jQuery对象,因此它可以用于链式调用。下面这个例子(使用prepend方法将在第三小节阐述):
//给文档中的div元素标号,从开始一直到div#last(包含边界值) $("div").each(function(idx){//找到div元素,然后遍历它们 $(this).prepend(idx + ":");//找到没一个元素前面插入索引值 if(this.id === "last") return false;//碰到#last元素时终止 });
尽管each()方法很强大,但是用的并不多,因为jQuery方法通常隐式遍历匹配元素集并操作它们。需要使用到each()的典型场景需要不同的方式操作匹配的元素集并操作它们。需要使用each()的典型场景是需要不同的方式来操作匹配的元素。即便如此,也不需要调用each(),因为jQuery的一些方法允许传递回调函数。
在ECMAScript数组方法规范之前,jQuery类库就已经存在了。jQuery定义了几个方法,其功能和ECMAscript5方法功能类似。jQuery的map()方法和Array.prototype.map()的方法很相近。
它接受回调函数作为参数,并为jQuery对象中的没一个元素都调用回调函数,同时将回调函数的返回值收集起来,并将这些返回值封装成一个新的jQuery对象返回。map()调用回调函数的方式和each()方法相同:元素作为this值和第二参数传入,元素的索引值作为第一参数传入。如果回调函数返回null或undefined,该值将被忽略,在本次调用中不会有任何新元素添加到新的jQuery对象中。如果回调函数返回数组或类数组对象(比如jQuery对象),将会扁平化它并将其中的元素一个个添加到新的jQuery对象中。注意:由map()返回的jQuery对象可以不包含文档元素,但它依旧可以像类数组对象一样使用:例如
//找到所有标题元素,映射到它们的id,并转化为真实数组,然后排序 $(":header").map(function(){return this.id;}).toArray().sort();
除了each()和map()之外,jQuery的另外一个基础方法是index()。该方法接受一个元素作为参数,返回值是该元素在此jQuery对象中的索引值,如果找不到的话,则返回-1。显然,受jQuery的典型风格影响,index()方法有多个重载版本。传递一个jQuery对象作为参数,index()方法会对该对象的第一个元素进行搜索。如果传入的是字符串,index()会把它当成css选择器,并返回该jQuery对象中匹配该选择器的一组元素中的第一个元素的索引值。如果什么参数都不传入,index()方法返回该jQuery对象中第一个毗邻元素的索引值。
这里要讨论的最后一个通用的jQuery方法是is()。它接收一个选择器作为参数,如果选中元素中至少有一个匹配该选择器时,则返回true。可以在each()回调函数中使用它,例如:
if($(this).is(":hidden")) return;//跳过隐藏元素 //对可见元素做点什么 });
2.jQuery的getter和setter
jQuery对象上最简单、最常见的操作是获取(get)或设置(set)HTML属性、css样式、元素内容和位置高宽的值。该节讲述这些方法。首先,让我们对jQuery中的getter和setter方法有个概要理解。
在阅读本节接下来的内容时,请将对getter和setter的概要要理解牢记于心。下面的每一节会讲述jQuery getter/setter方法中的一个重要类别
i.获取和设置HTML属性
attr()方法是jQuery中用于HTML属性的getter/setter,它符合上面描述的概要和理解中的每一条。attr()处理浏览器的兼容性和一些特殊情况,还让html属性名和javascript属性名可以等同使用(当二者存在差异时)例如,可以使用"for"也可以使用"htmlFor",可以使用"class"也可以使用"className".一个相关函数是removeAttr(),可用来从所有选中元素中移除某个属性。下面是一些例子:
$("form").attr("action"); $("#icon").attr("src", "ico.gif"); //设置src属性 $("#banner").attr({ //一次性设置4个属性 src: "banner,gif", alt: "advertisenment", width: 720, height: 64 }); $("a").attr("target","_blank");//使所有链接在新窗口打开 $("a").attr("target",function(){ //非站内的链接在新窗口中打开 if(this.host == location.host) return "_self" else return "_blank"; }); $("a").attr({target:function(){...}}); //可以像这样传入函数 $("a").removeAttr("target");//
ii.获取和设置CSS属性
css()方法和attr()方法很类似, 只是css()方法作用于元素的css样式,而不是元素的HTML属性。在获取元素样式值时,css()返回的是当前样式,而不是元素的HTML属性。在获取样式值时,css()返回的是当前样式(或称为“计算”样式,参考14章4节):返回值可能来自style属性也可能来自样式表,注意:不能获取复合样式的值,比如:font或margin。而获取单个样式的值,font-weiht、margin-top等、
在设置样式时,css()方法会将样式简单添加到元素的style属性中。css()方法允许在CSS样式名中使用联系的字符串("background-color")或使用驼峰式javascript样式名("backgroundColor")。在获取样式值时,css()会把数值转换为带有点位的后缀字符串返回。而在设置样式值时,则会将数值转化为字符串,在必要的时候添加"px"等单位后缀。
$("h1").css("font-weight"); $("h1").css("font-size"); $("h1").css("font"); //错误,不能获取复合样式的css属性 $("h1").css("font-variant", "small-caps"); //该属性主要用于定义小型大写字母文本 $("div").css("border", "solid black 2px"); //这样设置复合属性是没问题的 $("h1").css({ backgroundColor: "black", //一次性设置多个样式 color: "white" }); //让所有的<h1>字体大小增加25% $("h1").css("font-size", function(i, curval) { return Math.round(1.25 * parseInt(curval)); });
iii.获取和设置css类
回忆一下,class属性值(在javascript里通过className访问)会被解析称为一个由空格风格的CSS类名列表。通常,我们想要往列表中添加、删除某一项,或判断某一想是否在列表中,而不是将该列表替换为另一个。因此,jQuery定义了一些便捷方法来操作class属性。addClass()和removeClass()用来从选中元素中添加和删除类。toggleClass()的用途是,当元素还没有某些类时,给元素添加这些类,反之,则删除。hasClass()用来判断某类是否存在,下面是一些例子:
//添加css类 $("h1").addClass("hilite"); //给所有的h1添加一个class $("h1+p").addClass("hilite firest"); //给h1后面的p添加两个类 $("section").addClass(function(n){ //传递一个元素添加自定义类 return "section" + n; //每个元素添加自定义类 }); //删除CSS类 $("p").removeClass("hilite");//从所有p元素中删除一个类 $("p").removeClass("hilite first");//一次性删除多个类 $("section").removeClass(function(n){ //删除元素中自定义的类 return "section" + n; }); $("div").removeClass(); //删除所有<div>中的所有类 //切换CSS类 $("tr:odd").toggleClass("oddrow");//如果该类不存在则添加 //存在则删除 $("h1").toggleClass("big samll"); //一次切换两个类 $("h1").toggleClass(function(n){ //切换函数计算出来的类 return "big bold h1-" +n; }); $("h1").toggleClass("hilite",true); //作用类似addClass $("h1").toggleClass("hilite",false); //作用类似removeClass //检测css类 $("p").hasClass("first"); //是否所有p元素都有该类? $("#lead").is(".first"); //功能和上面类似 $("#lead").is(".first.hilite"); //is()比hasClass()更灵活
注意:hasClass()不如addClass()、removeClass()、toggleClass()灵活。hasClass()只能接受单个类名作为参数,并且不支持函数参数。当选中元素中任意元素有指定的CSS类时,HasClass()返回true;如果什么元素都没有,则返回false,上面的is()方法更灵活,可以做同样的事情。
jQuery的这些方法和14章5节的classList方法类似,只是jQuery方法可以工作在所有的浏览器中,而不是支持css3的浏览器。此外,毫无疑问jQuery的方法可以操作多个元素并支持链式调用。
iiii.获取和设置HTML表单值
val()方法用来设置和获取HTML表单元素的value属性,还可以用于获取和设置复选框、单选按钮以及<select>元素的选中状态。
$("#surname").val(); //获取surname 文本域的值 $("#usstate").val(); //从<select>中选取单一值 $("select#extras").val(); //从<select multiple>中获取一组值 $("input:radio[name=ship]:checked").val(); //获取单选选中按钮的值 $("#email").val("invalid email address"); //给文本域设置值 $("input:checkbox").val(["op1","op2"]) //选中带有这些名字或值的复选框 $("input:text").val(function(){//重置所有文本域为默认值 return this.defaultValue; });
iiiii.设置和获取元素内容
text()和html()方法用来获取和设置元素的纯文本或HTML内容。当不带参数调用时,text()返回所有匹配元素的所有子孙文本节点的纯文本内容。该方法甚至可以发展在不支持textContent或innerText属性(13章5节ii)的浏览器中。
如果不带参数调用html()方法,它会返回第一个匹配元素的HTML内容。jQuery使用innerHTML属性来实现:x.html()和x[0].innerHTML一样高效
如果传入字符串给text()或html(),该字符串会用做该元素的纯文本或格式化的html文本内容,它会替换掉所有存在的内容。和其他setter方法一样,我们还可以传入函数,此函数用来计算表示新内容的字符串:
var title = $("head title").text(); //获取文本标题 var headline = $("h1").html(); //获取第一个<h1>元素的HTML $("h1").text(function(n,current){//给每一个标题添加章节号 return "§" + (n+1) + ":" + current });
iiiiii.获取和设置元素的位置高宽
在13章8节中我们知道通过一些技巧可以正确获取元素的大小和位置,尤其当浏览器不支持getBoundClientRect(13章8节ii)时,使用jQuery方法可以更简单的获取元素的大小和位置,并兼容所有浏览器。注意:本节描述的所有方法都是getter,只有少部分可以用作setter。
使用offset()方法可以获取或设置元素的位置 。该方法相对文档来计算位置值,返回一个对象,带有left和top属性,用来表示x和y坐标。如果传入带有这些属性的对象给该方法,它会给元素设置指定的位置。在有必要时,会设置CSS的position属性来是的元素可定位:
var elt = $("#sprite"); //选择要移动的元素 var position = elt.offset(); //获取当前元素的位置 position.top += 100; //改变y坐标 elt.offset(position); //从新设置位置 //将所有的<h1>元素向右移动,移动的距离取决于他们在文档中的位置 $("h1").offset(function(index, curpos) { return { left: curpos.left + 25 * index, top: curpos.top }; });
postion()方法很像offset()方法,但它只能用作getter,它返回的元素位置是相对于其偏移父元素的,而不是相对文档的。在13章8节iiiii中,我们知道任何一个元素都有一个offsetParent属性,其位置是相对的。定位元素总会当做其子孙元素的偏移父元素,但在某些浏览器下,也会把表格单元格等其他元素当成偏移父元素。jQuery只会把定位元素作为偏移父元素。jQuery对象的offsetParent()方法则会把每个元素的映射到最近的定位祖先元素或<body>元素。注意这些方法的名字并不很恰当:offset()返回元素的绝对位置,相对于文档的坐标来表示。而postion()则返回相对元素的offsetParent()的偏移量。
对于获取元素的宽度的getter有3个,获取的高度也有3个。
width()和height()方法返回基本的宽度和高度,不包含内边距 、边框和外边距。
innerWidth()和innerHeight()返回元素的宽度和高度,包含内边距的宽度和高度(“内”表示这些方法度量的是边框以内的尺寸)。
outerWidth()和outerHeight()通常返回的是包含元素内边距和边框的尺寸。如果向两个方法中任意一个传入true值,他们还可以返回包含元素外边距的尺寸。下面代码展现了如何获取一个元素的4种不同宽度:
var body = $("body"); var contenetWidth = body.width(); var paddingWidth = body.innerWidth(); var borderWidth = body.outerWidth(); var marginWidth = body.outerWidth(true); var padding = paddingWidth - contenetWidth; //左内边距和右内边距的和 var borders = borderWidth - paddingWidth; //左边框和右边框的和 var margins = marginWidth - borderWidth; //左外边距和右边距的和
width()和height()方法拥有其它4个方法(以inner和outer开头的方法)所没有的特性,首先,当jQuery对象的第一个元素是Window或Document对象时,width()和height()返回的是窗口的视口大小或文档的整体尺寸。其它方法只适用于元素,不适合窗口和文档。
另一个特性是width()和height()方法可以是setter()也可以是getter()。如果传递值给这些方法,他们会给jQuery对象中的每一个元素设置宽度或高度。(注意:不能给window和document对象对象设置宽度或高度。)如果要传入数值,会把它当成单位为像素的尺寸。如果传入字符串,会把它用作css的width和height属性的值,因此可以使用任何css单位。最后,和其他setter类似 ,可以传入函数,用来计算要设置的宽度或高度。
在width()和height()的getter和setter行为之间有个小的不对称。用作getter时,这些方法返回元素的内容盒子尺寸,不包括内边距、边框和外边距。用做setter()时,他们只是简单的设置CSS的width和height属性。默认的情况下,这些属性也指定内容盒子的大小。但是,如果一个元素的CSS box-szing属性(14.2.iii)设置为border-box,则width()和height()方法设置的尺寸包括内边距和边框。对于使用context-box作为盒模型的元素e,调用$(e).width(x).width()返回x值。然而,对于使用border-box模型的元素,这种情况下一般不会返回x值。
与位置尺寸相关的最后与对jQuery方法是scrollTop()和scrollLeft(),可获取或设置元素滚动条的位置。这些方法可以用在window对象以及Document元素上,当用在Document对象上时,会获取或设置存放改Document的window对象的滚动条位置。与其他setter不同,不可传递函数给scrollTop()和scrollLeft()。
可使用scrollTop()作为getter和setter,与height()方法一起,来定义一个方法:根据指定的页面数向上或向下滚动窗口
//根据页面数n来滚动窗口。 n可以是分数也或负数 function page(n){ var w = $(window); //将window封装为jQuery对象 var pagesize = w.height(); //得到页面大小 var current = w.scrollTop(); //得到当前滚动条的位置 w.scrollTop(current + n*pagesize);// 设置新的滚动条位置 }
iiiiiii.获取和设置元素数组
jQuery定义了一个名为data()的getter/setter方法 ,用来设置或获取与文档元素、Document或Window对象相关联的数据。可以将数据与任意元素关联是很重要和强大的一项能力:这是jQuery的事件处理程序注册和效果队列机制的基础,有时,我们还会再自己的代码中使用data()方法。
需要将数据与jQuery对象中的元素关联,传递名称和值两个参数给data()即可。还可以传递一个对象给data() setter,此时,该对象的每一个属性都将用做名/值对,用来与jQuery对象的元素关联。注意,传递对象给data()时,该对象的属性将替换掉与元素相关联的旧数据。其其它很多setter方法不同,data()不接受函数参数。当将函数作为第二参数传递给data()时,该函数会存储,就和其他值一样。
当然,data()方法也可以用做getter。当不带参数调用时,它会返回一个对象,含有与jQuery对象中的第一个元素相关联的所有名/值对。当传入一个字符串参数调用data()时,它会返回对已第一个元素与该字符串相关联的数据值。
removeData()方法从元素中删除数据。(使用data()设置值为null或undefined和实际上删除该值并不是同一回事。)如果传递字符串给removeData(),该方法会删除元素中与该字符串相关联的值。如果不带参数调用removeData(),它会删除与元素相关联的所有数据。
$("nodes").data("x",1);//设置一些数据 $("div.nodata").removeData("x"); //删除一些数据 var x = $('#nodes').data("x"); // 获取一些数据
jQuery还定义了data()和removeData()方法的工具函数形式。要给单一元素e关联数据,可以使用data()方法形式,也可以使用其函数形式:
$(e).data(...) //方法形式 $.data(e,...) //函数形式
jQuery的数据框架没有将元素数据当做元素的属性来存储,但它的确需要给元素添加一个特殊的属性来与数据关联。由于某些浏览器不允许添加属性到<applet>、<object>和<embed>元素中,因此,jQuery根本不允许给这些类型的元素关联数据。
3.修改文档结构
我们在2节iiiii节中我们知道html()和text()方法用来设置元素内容。本节将讲述能对文档做出更复杂的修改的方法。HTML文档表示为一棵节点树,而不是一个字符的线性序列,因此插入、删除、替换、操作不会像操作字符串和数组一样简单。接下来的内容会阐释用于文档修改的jQuery的各种方法。
i.插入和替换元素
让我们从基本的插入和替换方法开始。下面演示的每一个方法都接受一个参数,用于指定需要插入文档中的内容。该参数可以是用于指定新内容的纯文本或html字符串,也可以是jQuery对象、元素或文本节点。根据调用的方法不同,会在选中元素的里边,前面,或后面插入内容。如果待插入的内容是已存在于文档中的元素,会从当前的位置移走它。如果它需要插入多次,在必要的时会复制该元素。这些方法都返回自身的jQuery对象。注意,在replaceWith()运行后,该jQuery对象中的元素将不存在于文档中:
$("#log").append("<br/>" + message); //在#log元素结尾处添加内容 $("h1").prepend("§"); //在每个<h1>的起始处添加章节标识符 $("h1").before("<hr/>"); //在每个<h1>的前面添加水平线 $("h1").after("<hr/>"); //在每个<h1>的后面添加水平线 $("hr").replaceWith("<br/>") //将<hr/>元素替换为<br/> $("h2").each(function(){//将<h2>替换为<h1>,保持内容不变 var h2 = $(this); h2.replaceWith("<h1>" + h2.html() + "</h1>"); }); // after()和before()也可用在文本节点上 // 这是给每个<h1>的开头添加上章节标识符的另一种方法 $("h1").map(function(){ return this.firstChild; }).before("§")
这5个用于结果修改的方法都接受函数参数,原理啊计算出需要插入的值。和平常一样,如果传入参数,该函数会为每个选中的元素调用一次。this指向该元素,在jQuery对象中元素的索引值将作为第一参数。对于append()、prepend()个replaceWidth(),第二参数将是该元素当前内容的HTML()字符串形式。对于before()和after(),该函数在调用时没有第二参数。
上面演示的5个方法都在目标元素上调用,并传入需要插入的内容作为参数。这5个方法中的每一个都可以找到另一个方法来实现差不多一样的功能,只要采用不同的操作方式即可:在内容上调用,并传入目标元素作为参数。下面展示了这些方法对:
操作 | $(target).method(content) | $(content).method(target) |
在目标元素的结尾处插入内容 | append() | appendTo() |
在目标元素的起始处插入内容 | prepend() | preprendTo() |
在目标元素的后面插入内容 | after() | insertAfter() |
在目标元素的前面插入 内容 | before() | insertBefore() |
将目标元素替换为内容 | replaceWith() | replaceAll() |
想要理解这几个方法,有几个重要事项
如果传递字符串给第二列中的方法,会把它当做需要插入的HTML字符串。如果传递字符串给第三列中的方法,会把它当做选择器,用来标识目标元素。(也可以直接传入jQuery对象、元素或文本节点来指明目标元素)
第三列的方法不接受函数参数,第二栏中的方法可以。
第二列中的方法返回调用自身的jQuery对象。该jQuery对象中的元素有可能有新内容或新兄弟节点,但这些元素自身没有修改。第三列中的方法在插入的内容上调用,返回一个新的jQuery对象,表示插入操作后的新内容。特别注意,当内容被插入多个地方时,返回的jQuery对象将为每一个地方保留一个元素。
上面例举了不同点,下面的代码将实现与上面代码一样的操作,使用的是第三列的方法来替代第二列中的。注意在第二行中的代码不能传入纯文本(不带任何<>括号来标识它为HTML)给$()方法--它会被当做选择器。因此,必须显式创建需要插入的文本节点:
$("<br/> + message").appendTo("#log"); //添加html到#log中 $(document.createTextNode("§")).prependTo("h1"); //给所有<h1>添加文本节点 $("<hr/>").insertBefore("h1"); //在所有的<h1>前面插入水平线 $("<hr/>").insertAfter("h1"); //在所有的<h1>后面插入水平线 $("<br/>").replaceAll("hr"); //将<hr/>替换为<br/>
ii.复制元素
如上所述,如果插入的元素已经是文档的一部分,这些元素只会简单地移动而不是复制到新位置。如果元素到插入不止一个位置,jQuery在需要时会复制元素,但是当只插入一个位置时,是不会进行复制操作的。如果想复制元素到新位置而不是移动它,必须首先用clone()创建并返回每一个选中元素(包含元素所有子孙)的一个副本。clone()创建并返回一个选中元素(包含元素所有子孙)的一个副本。返回的jQuery对象的元素还不是文档的一部分,可以用上一节的方法将其插入文档中。
//给文档结尾添加一个带有"linklist" id的新div $(document.body).append("<div id = 'linklist'><h1>List of Links</h1></div>"); //将文档中所有链接复制并插入新div中 $("a").clone().appendTo("#linklist"); //在每一个链接后面插入<br/>元素,使其独立行显示 $("#linklist > a").after("<br/>");
clone()不会复制事件处理程序(见第4节)和与元素相关联的其它数据(2节iiiiiii)。如果想复制这些额外的数据,请传入true参数。
iii.包装元素
插入HTML文档的另一只类型涉及在一个或多个元素中包装新元素。jQuery定义了3个包装函数。wrap()包装每一个选择元素。wrapInner()包装每一个选中元素的内容。wrapAll()则将选中元素作为一组数组来包装。这些方法通常传入一个新创建的包装元素或用创建新包装元素的HTML字符串。如果需要,HTML字符串可以包含多个嵌套元素,但必须是单个最内层的元素。如果传入函数给这些方法,它会在每个元素的上下文中调用一次,this指向该元素,元素的索引值是唯一参数,应该返回需要返回表示包装元素的字符串、Element或jQuery对象。下面是些例子:
//用<i>元素包装所有<h1>元素 $("h1").wrap(document.createElement("i")); //=> <i><h1></h1></i> //包装所有<h1>元素的内容,使用字符串参数更简单 $("h1").wrapInner("<i/>") //=> <h1><i>1</i></h1> // 将第一个段落包装在一个锚点和div里 $("body>p:first").wrap("<a name='lead'><div class='first'></div></a>"); // 将所有其它段落包装在另一个div里 $("body>p:not(:first)").wrapAll("<div class='rest'></div>");
iiii.删除元素
除了插入和替换操作,jQuery还定义了用来删除元素的方法。empty()会删除每个选中元素的所有子节点(包括文本节点),但不会修改元素自身。对比而言,remove()方法会从文档中移除选中元素(以及 元素的所有的 内容。)通常不带参数调用remove(),此时会从文档中移除jQuery对象中的所有元素。然而,如果传入一个参数,该参数会被当成选择器,jQuery对象中只有匹配该选择器的元素才会被移除。(如果只想将元素从选中元素集中移除,而不需要从文档中移除时,请使用filter()方法,该方法会在8.ii中讲述)注意,将元素重新插入文档钱,移除操作是没必要的:简单地将其插入新的位置,就会移除他们。
remove()方法会移除所有的时间处理程序(参加4节)以及绑定到元素上的其它数据(2.iiiiiii)。detach()方法和remove()类似,但不会移除事件处理程序和数据。想临时从文档中移除元素以便后续再插入时,detach()可能会更有用。
最后,unwarp()方法可以用来实现元素的移除,其方式是wrap()或wrapAll()方法的反操作:移除每一个选中元素的父元素,不影响选中元素及兄弟节点。也就是说,对于每一个选中的父元素,它替换改元素的父节点为父节点的子节点。与remove()和detach()不同,unwrap()不接受可选的选择器参数。
4.使用jQuery处理事件
在15章我们知道,处理事件时有一个难点是IE(IE9以下),实现了一个与其他浏览器不同的事情API,为了解决这一难点,jQuery定义了一个统一事件API,可工作在所有的浏览器中。jQuery API更具简单的形式,比标准或IE的事件API更易使用。jQueryAPI还更具有复杂,功能齐全的形式,比标准的API更强大。接下来章节我们会更详细阐述。
i.事件处理程序的简单注册
jQuery定义了简单的事件注册方法,可用于常用和普通的每一个浏览器事件。比如,给单击事件注册一个处理程序,只需要调用click()方法:
$("p").click(function(){$(this).css("background-color","gray");});
调用jQuery的事件注册方法可以给所有选中元素注册处理程序。很明显,这笔addEventListener()或attachEvent()一次注册一个程序简单很多。
下面是jQuery定义的简单的事件处理程序注册的方法:
blur() | focusin() | mousedown() | mouseup() |
change() | focusout() | mouseenter() | resize() |
click() | keydown() | mouseleave() | scroll() |
dbclick() | keypress() | mousemove() | select() |
error() | keyup() | mouseout() | submit() |
focus() | load() | mouseover() | unload() |
这些注册方法大部分都用于在第15章已经熟悉的常见事件类型。下面按顺序给出了一些注意事项。focus和blur事件不支持冒泡,但focusin和focusout支持,jQuery确保这些事件在所有的浏览器下都支持。相反地,mouseover和mouseout事件支持冒泡 ,但这经常不方便,因为很难知道鼠标是从自己感兴趣的元素移开了,还只是从改元素的子孙元素移开了。mouseenter和mouseleave是非冒泡事件,可以解决刚才的问题。这几个事件类型最初是IE引入的,jQuery保证它们在所有的浏览器下正常工作。
resize和unload事情类型只在Window对象中触发,如果想要给这两个事件类型注册处理程序,应该在$(window)上调用resize()和unload()方法。scroll()方法经常也用$(window)对象上,但它也可以用在有滚动条的任何元素上(比如,当css的overflow属性设置为scroll或"auto"时)。load()方法可以在$(window)上调用,用来给窗口加载事件处理程序,但经常更好的选择是,直接将初始化函数传给1.i所示的$()。当然,还可以在iframe和图片上使用load()方法。注意,用不同的参数调用时,load()方法可以用在<img>元素上,用来注册当图片加载失败时的调用程序。error()不应该用于设置12章6节描述的窗口onerror属性
除了这些简单的事件注册方法外,还有两个特殊形式的方法,有时很有用。hover()方法用来给mouseenter和mouseleave事件注册处理程序。调用hover(f,g)就和调用mouseenter(f),然后调用moseleave(g)一样。如果仅传入一个参数给hover(),该参数函数会同事用做enter和leave事件的处理程序。
另一个特殊事件注册方法是toggle()。该方法将事件处理程序函数绑定到单击事件。可指定两个或多个处理程序函数,当单击事件发生时,jQuery每次会调用一个处理程序函数。例如,如果调用toggle(f,g,h),第一次单击时触发f(),第二次会调用g(),第三次调用会h(),然后调用f()来处理第四次单击事件。小心使用toggle():我们将在5.i看到,该方法可以用来显示或隐藏选中的元素(也就是说:切换选中元素的可见性)。
4节iiii中,我们会用到其它更通用的方式来注册事件处理程序,本节最后,让我们学习一个更简单更便捷的处理程序注册方法。
回忆下,可以传递HTML字符串给$()方法来创建该字符串所描述的元素,还可以传入一个对象(当做第二个参数),该对象由属性组成,这些属性可以设置到新创建的元素上。这第二个参数可以是传递给attr()方法的任意对象。此外,如果这些属性中有任何一个与上面举例的事件注册方法同名,该属性值就会当作处理程序函数,并注册命名事件类型的处理程序。例如:
$("<img />", { src: img_url, alt: img_description, className: "trans_img", click: function() { $(this).css("opacity", "50%"); } });
ii.jQuery事件处理程序
上面例子中事件处理程序函数被当做不带参数以及不返回值的。像这样书写事件处理程序非常正常,但jQuery调用每一个事件处理程序时的确传入了一个或多个参数,并且对处理程序的返回值进行了处理。需要知道的最重要的一件事情是,每个事件处理程序都传入一个jQuery事件对象作为第一个参数。该对象的字段提供了与该事件相关的详细信息(比如鼠标的指针坐标)标准事件的的属性在第15章描述过。jQuery模拟标准Event对象,即便在不支持的标准事件对象的浏览器中(像IE8及其以下),jQuery事件对象在所有的浏览器拥有一组相同的字段。在这4.iii节会详细描述
通常,调用事件处理程序时只带有事件对象这个唯一参数,如果用tigger()(参见4.iiiii)显式触发事件,可以传入额外的参数数组。这样做时,这些参数会在第一个事件对象参数之后传递给事件处理程序。
不管他们是如何注册的,jQuery事件处理程序函数的返回值始终有意义。如果处理程序返回false,与该事件相关联的默认行为,以及该事件接下来的冒泡都会被取消,也就是说,返回false等同调用Event对象的preventDefault()和stopPropagation()方法。同样,当事件处理程序返回一个值,(非undefined值)时,jQuery会将该值存储在Event对象的result属性中,该属性可以被后续调用的事件处理程序访问。
iii.jQuery事件对象
jQuery通过定义自己的Event对象来隐藏浏览器之间的实现差异。当一个jQuery事件处理程序被调用时,总会传入一个jQuery事件对象作为其第一个参数。jQuery事件对象主要以W3C标准为基础,同时它实现了一些实际的事件标准。jQuery会将以下所有字段从原生Evenet对象中复制到jQuery Event对象上(尽管对于特定事件类型来说,有些字段为undefined)
altKey | ctrlKey | newValue | screenX |
attrChange | currentTarget | offsetX | sceenY |
attrName | detail | offsetY | shiftKey |
bubbles | eventPhase | origainaTarget | srcElement |
button | formElement | pageX | target |
cancelable | keycode | pageY | toElement |
charCode | layerY | relateNode | wheelData |
clientX | layerY | relateNode | wheelDelta |
clientY | metaKey | relatedTarget | which |
除了这些属性,Event对象还定义了以下方法
preventDefault() | isDefauletPrevented |
stopPropagation | isPropagationStopped |
stopImmediatePropagation | isImmediatePropagationStopped |
这些事件和属性和方法大部分在第15章介绍过,并且在第四部分ref-Event中有详细的文档说明。对于一部分字段,jQuery做了特殊处理,使其再所有浏览器中行为一致,值得我们留意:
metaKey
如果原生事件对象没有metaKey属性,jQuery会使其与ctrlKey属性的值一样。在Mac os中,Command键设置meta键的属性
pageX和pageY
如果 原生事件对象没有定义这两个属性,但定义了鼠标指针的视口坐标clientX和clientY,jQuery会计算出鼠标指针的文档坐标并把他们存储在pageX和pageY中。
target,currentTarget,relatedTarget
target属性表示在其上发生事件的元素文档。如果原生事件对象的目标是文本节点,jQuery返回的目标会替换为包含文本节点的元素。currentTarget是当前正在执行的事件处理程序所注册的元素,与this应该始终一样。
如果currentTarget和target不一样,那么正在处理的事件是从触发它的元素冒泡上来的,此时使用is()方法来检测target元素可能会很有用。
if($(event.target).is("a")) return; //忽略在链接上启动的事件
涉及mouseover和mouseout等过渡事件时,relatedTarget表示其他元素。例如,对于mouseover事件,relatedTarget属性指鼠标指针移开的元素,target则是鼠标指针悬浮的元素。如果原生事件对象没有定义relatedTarget但定义了toElement和formElement,则会从这些属性中得到relatedTarget.
timeStamp
事件发生时的时间,单位是毫秒,有Data.getTime()方法返回。这个字段是jQuery自身设置的,可以解决fireFox中长期存在的一个bug
which
这是一个非标准事件属性,jQuery做了统一化处理,使其可以用来指明在事件发生起见,按下的是那个鼠标按键或键盘按键。对于键盘事件来说,如果原生事件没有定义which,但定义了charCode或keyCode,which将被设置为定义过的charCode或keyCode。对于鼠标事件来说,如果which没有定义但定义了button属性,会根据button的值来设置which。0表示没有按钮按下。1表示鼠标左键按下,2表示鼠标中键按下,3表示鼠标右键按下。(注意:单击鼠标右键时,有些浏览器不会产生鼠标事件)
此外,jQuery Event对象的以下对象的字段是特定于jQuery添加的,有时会很有用。
data
如果注册事件处理程序时指定了额外的数据(参加17.4.iiii),处理程序可以用该字段值来访问。
handler
当前正在被调用的事件处理程序函数的引用
result
该事件最近调用的处理程序的返回值,忽略没有返回值的处理程序
originalEvent
浏览器上生成的原生事件对象的引用。
iiii.事件处理程序的高级注册
我们已经看到,jQuery定义了相当多简单的方法来注册事件处理程序。这些方法都是简单地调用单一的、更复杂的方法bind()来为命名事件类型绑定处理程序,该处理程序会绑定到jQuery对象中的每一个元素上。直接使用bind()可以让我们使用事件注册的高级特性,这些特性在较简单的方法是不可以用的。(jQuery使用术语"bind"来表示事件注册处理程序的注册。ECMAScript5(以及不少javascript框架)给函数定义了bind()方法,参见8.7.iiii,使用"bind"术语表示对象与函数之间的关联,这些函数会在这些对象上调用。Function.bind()方法的jQuery版本是一个名为jQuery.proxy()的工具函数,我们在本章第7节中会讲解它)
在最简单的形式下,bind()需要一个事件类型字符串作为其第一个参数,以及一个事件处理程序函数作为第二个参数。事件注册的简单方法使用该形式的bind()。例如,调用$('p').click(f)等价
$("p").bind('click',f);
调用bind()时还可以带有第三个参数。在这种形式下,事件类型是第一个参数,处理程序是第三个参数。在这两个参数之间可以传入任何值,jQuery会在调用处理程序钱,将指定的值设置为Event对象的Data属性。通过这些方法传递额外的数据给处理程序,不要使用闭包,有时很有用。
bind()还有其它高级特性,如果第一个参数是由空格分隔的事件类型列表,则处理程序函数会为每一个命名的时间类型注册。例如,调用$("a").hover(f),等同于
$("a").bind('mouseenter mouseleave',f)
bind()的另外一个重要特性是允许为注册的事件处理程序指定命名空间。这使得可以定义处理程序组,能方便后续或卸载特定命名空间下的处理程序。处理程序的命名空间对于开发者可服用jQuery代码的类库或模块对程序员来说特别有用。事件命名空间类似css的类选择器。要绑定事件处理器到命名空间时,添加句点(.)和命名空间名到事件类型字符串中即可:
//作为mouseover处理程序在命名空间"myMode"中把f绑定到所有元素 $('a').bind('mouseover.myMod', f);
甚至还可以给处理程序分配多个命名空间,如下所示:
//在命名空间"myMode"和"yourMod"中作为mouseout处理程序绑定f $("a").bind("mouseout.myMod.yourMod",f)
bind()的最后一个特性是,第一个参数可以是对象,把该对象事件名映射到处理程序函数。再次使用hover()方法举例,调用$("a").hover(f,g)等价于
$("a").bind({mouseenter:f,mouseleave:g});
当使用bind()的这种形式时,传入对象的属性名可以是空格分隔的事件类型的字符串,也包括命名空间。如果在第一个对象参数之后还指定了第二个参数,其值会用做每一个事件绑定的数据参数。
jQuery还有另外一个事件处理程序注册的方法。调用one()方法就和bind()一样,二者的工作原理也类似 ,处理在调用事件处理程序之后会自动注销它。这意味着,和该方法名字暗示一样,使用one()注册的事件处理器永远只会触发一次。
使用addEventListener()参见15章2.iii可以注册捕获事件处理程序,bind()和one()没有该特性。IE(IE9以下版本)不支持捕获处理程序,jQuery不打算模拟该特性。
iiiii.注销事件处理程序
使用bind(或任何更简单的时间注册方法)注册事件处理程序后,可以使用unbind()来注销它,以避免在将来的事件中触发它。(注意,unbind()只注销用bind()和相关jQuery方法注册的事件处理程序。通过addEventListener或IEattachRvent()方法注册的处理器不会被注销,并且不会移除通过onclick和onmouseover等元素属性定义的处理程序。)不带参数时,unbind()会注销jQuery对象中所有元素的(所有事件类型的)所有事件处理程序:
$("*").unbind();//从所有元素中移除所有jQuery事件处理程序
带有一个字符串参数时,由该字符串指明事件类型(可以是多个,当字符串包含有多个名字时)所有处理程序会从jQuery对象的所有元素中取消绑定:
//从所有a元素中取消所有mouseover和mouseout处理程序 $("a").unbind("mouseover mouseout");
这是很粗略的方法,不应该在模块化代码中使用,因为模块的使用者有可能使用其他模块,在其他模块中有可能在相同的元素上给相同的事件类型注册了其它处理程序。如果模块使用命名空间来注册事件处理程序,则可以使用unbind(),传入一个参数,来做到只注销命名空间下的处理程序:
//取消绑定在mymode命名空间下所有mouseover和mouseout处理程序 $("a").unbind("mouseover.myMode mouseout.myMod"); //取消绑定在"myMod"命名空间下的所有事件类型处理程序 $("a").unbind(".myMod") //取消绑定在ns1和ns2命名空间下的单击处理程序 $("a").unbind("click.ns1.ns2")
如果想小心地只取消绑定自己的事件处理程序,但没有使用命名空间,必须保留事件处理程序函数的一个引用,并使用unbind()带两个参数的版本。在这种形式下,第一个参数是事件类型字符串(不带命名空间),第二个参数是处理程序函数:
$("#mybutton").unbind("click",myClickHandler);
使用这种方式调用时,unbind()从jQuery对象的所有元素中注销特定类型的指定事件处理程序函数。注意,即使使用有3个参数的bind()通过额外的数据值注册事件处理程序,也可以使用有两个参数的unbind()事件来取消绑定它们。
可以传递单一对象参数给unbind()。在这种情况下,unbind()会轮询为该对象的美怡属性调用一次。属性名会用做事件类型字符串,属性值会用做处理程序函数:
$("a").unbind({
mouseover:onmouseoverHandler,
mouseout:onmouseoutHandler
});
最后,还有一种方式来调用unbind()。如果传递一个jQuery Event对象给unbind(),它会取消绑定传入事件的处理程序,调用unbind(ev)等价于unbind(ev.type,eb.handler)。
iiiiii.触发事件
当用户使用 鼠标、键盘或触发其它事件类型时,注册的事件处理程序会自动调用。然而,如果能手动触发事件,有时会很有用。手动触发事件最简单的方式是不带参数调用事件注册的简单方法(比如click()或mouseover()).与很多jQuery方法可以同时getter和setter一样,这些事件方法在带有一个参数时会注册事件处理程序,不带参数调用时则会触发事件处理程序。例如:
$("#my_form").submit();// 就和用户单击提交按钮一样
上面的submit()方法自己合成了一个Event对象,并触发了给submit事件注册的所有事件处理程序。如果这些事件处理程序都没有返回false或调用Event对象的preventDefault(),事件上将提交改表单。注意,通过这种方式手动调用时,冒泡事件依旧会冒泡。这意味这触发一组选中元素事件,同时也会触发这些元素祖先节点的处理程序。
需要特别注意:jQuery的事件触发方法会触发所有使用jQuery事件注册方法注册的处理程序,也会触发通过onsubmit等HTML属性或Element属性定义的处理程序。但是,不能手动触发使用addEventListener()或attachEvent()注册的事件处理程序(当然在真实时间触发时,这些处理程序依旧会调用)
同时需要注意的,jQuery的时间触发机制是同步的--不涉及事件队里。当触发一个事件时,在调用的触发方法返回之前,事件处理程序会立刻调用。如果触发了一个单机事件,被触发的处理程序又触发了一个submit事件,所有匹配的submit处理程序会调用下一个单机处理器之前调用。
绑定和触发事件时,submit()这种方法会很便捷,但就如jQuery定义了一个更通用的bind()方法一样,jQuery也定义了一个更通用的trigger()方法。通常,调用triggter()时会传入事件类型字符串作为第一个参数,trigger()会在jQuery对象中的所有元素上触发为该类型事件注册的所有处理程序。因此,上面的submit()调用等价于:
$("my_form").trigger("submit");
与bind()和unbind()方法不同,在传入的字符串中不能指定多个事件类型。然而,与bind()和unbind()一样的是,可以指定事件命名空间来触发仅在该命名空间中定义的处理程序。如果只想触发没有命名空间的事件处理程序,在事件类型后添加一个感叹号就行。通过onclick等属性注册的处理程序被认为是没有命名空间的:
$("button").trigger("click.ns1"); // 触发某个命名空间下的单击处理程序 $("button").trigger("click1"); // 触发没有命名的单击处理程序
除了给trigger()传入事件类型字符串作为第一个参数,还可以传入Event对象(或任何由type属性的对象)。type属性会用来判断触发什么类型的处理程序。如果传入的是jQuery事件对象,该对象会传递给触发的处理程序。如果传入的是普通对象,会创建一个新的jQuery Event对象,普通对象的属性会添加到新对象中。这样,可以很容易传递额外数据给事件处理程序:
//button1的事件处理程序触发button2上的相同的时间 $("#button1").click(function(e){$("#button2").trigger(e);}); // 触发事件时,添加额外的属性给事件对象 $("#button1").trigger({type:"click",synthetic:true}); // 该处理程序检测额外属性来区分是真实时间还是虚假事件 $("#button1").click(function(e){ if (e.syntheic) {...}; });
给事件处理程序传递额外数据的另一种方式是,在手动触发事件时,给trigger()传入第二个参数。给trigger()传入的第二个参数会成为每个触发事件处理程序的第二个参数。如果传入数组作为第二个参数,数组的每一项会作为独立参数传递给触发的处理程序:
$("#button1").trigger("click",true); //传入单一额外参数 $("#button1").trigger("click",[x,y,z]);//传入三哥额外的参数
有时,会想触发给定事件类型的所有处理程序,而不管这些处理程序是绑定到什么文档元素上的。这时可以使用$('*')来选中所有元素,然后对调用trigger(),可是这样做非常低效。更好的方是,使用jQuery.event.trigger()工具函数,来全局触发事件。该函数接受参数和trigger()方法一样,但在整个文档中触发指定事件类型的事件处理程序时更高效。注意,以这种方式触发的“全局事件”不会冒泡,并且只会触发使用jQuery方法注册的处理程序(不包括onclick等DOM属性注册的事件处理程序)。
trigger()(及调用它的便捷方法)在调用事件处理程序后,会执行与触发事件相关联的默认操作(假设事件处理程序没有返回false或调用事件对象的preventDefault())。例如,触发一个<form>元素的submit事件,trigger()会调用该表单的submit()方法,如果触发一个元素的focus事件,tigger()会调用元素的focus()方法。
如果想调用事件处理程序,但不调用默认操作,可以使用triggerHandler()替代trigger()。该方法和trigger()类型,除了首先会调用Event对象的preventDefault()和cancelBubble()方法。这意味着通过triggerHandler()手动触发的事件不会冒泡,也不会执行相关联的默认操作。
iiiiiii.自定义事件
jQuery的事件管理体系是为标准事件而设计的,比如web浏览器产生的鼠标单击和按键按下。但是,jQuery不限于这些事件,你可以使用任何想用的字符串来作为事件类型名称。使用bind()可以注册这种“自定义事件”的处理程序,使用trigger()可以调用这些处理程序。
对于书写模块化代码,实现发布/订阅模型或观察者模式时,这种自定义处理程序的间接调用被证明是非常有用的。使用自定义事件时,通常你会发现,使用jQuery.event.trigger()函数替代trigger()来全局触发处理器会更有用。
// 用户单击"logoff"按钮是,广播一个自定义事件 // 给任何需要保存状态的感兴趣的观察者,然后导航到logoff页面 $("#logoff").click(function(){ $.event.trigger("logoff"); //广播一个事件 window.location = "logoff.aspx"; //导航到新页面 });
在6.iiii节我们将看到jQuery的ajax方法会像上面这样广播自定义自定义事件,以通知感兴趣的监听器。
iiiiiiii.实时事件
bind()方法绑定事件处理程序到指定的文档元素,就与addEventListner()和attachEvent()(参见第17章)一样。但是,使用jQuery的web应用经常动态创建新元素。如果使用bind()给文档中的所有<a>绑定了事件处理程序,接着又创建了带有<a>元素的新文档内容,这些信元素和老元素不会拥有相同的事件处理程序,其行为将不一样。
jQuery使用“实时事件”来解决这一问题。要使用实时事件,需要使用delegate()和undelegate()来替代bind()和unbind()。通常,在$(document)上调用delegate(),并传入一个jQuery选择器字符串、一个jQuery事件类型字符串以及一个jQuery事件处理程序函数。它会在document或window上(或jQuery对象中的任何元素上)注册一个内部处理程序。当指定类型的事件冒泡到该内部处理程序时,它会判断事件目标(该事件所发生在的元素)是否匹配选择器字符串。如果匹配,则调用指定的处理程序函数。因此,为了同时处理老的和新创建的<a>元素上的mouseover事件,可能需要下面这样注册处理程序:
$(document).delegate("a","mouseover",linkHandler)
否则,需要使用bind()来处理文档中的静态部分,然后使用delegate()来处理动态修改的部分:
//静态链接的静态事件处理程序 $("a").bind("mouseover",linkHandler); //文档中动态更新的部分使用实时事件处理程序 $(".dynamic").delegate("a","onmouseover",linkHandler);
与bind()方法拥有三个参数版本来指定事件对象的data属性一样,delegate()方法拥有4参数版本用来干同样的事情。使用这种版本时,将数据值作为第三参数传入,处理程序函数则作为第4参数。
理解这点很重要:实时事件依赖于事件冒泡。当事件冒泡到document对象时,它有可能已经传递给很多静态事件处理程序。如果这些处理程序中有任何一个调用了Event对象的cancalBubble(),实时事件处理程序将永远不会调用。
jQuery定义了一个名为live()的方法,也可以用来注册实时事件,live()比delegate()更难理解一些,但与bind()一样,live()也有两参数和三参数的调用形式,并且在实际中用得更普遍。上面对delegate()的两个调用,也可以用live()来写 ,如下所示:
$("a").live("mouseover",linkHandler);
$("a",$(".dynamic")).live("mouseover",linkHandler);
在jQuery对象上调用live()方法时,该对象中的元素实际上并没有使用。真正有关系的是用来创建jQuery对象的选择器字符串和上下文对象(传递给$()的第一个和第二个参数)。jQuery对象的context()和selector属性来使得这些值可用。(见本章1.ii内容)通常,仅带一个参数的调用$()时,context是当前文档 。因此,对于jQuery对象x,下面两行代码做的事情是一样的:
x.live(type,Handler);
$(x.context).delegate(x.selector,type,handler);
要注销实时处理程序,使用die()或undelegate()。可用带一个或两个参数调用die()。带有一个事件类型参数时,die()会移除匹配选择器和事件类型的所有实时事件处理程序。带有事件类型和处理程序函数参数时,它只会移除掉指定的处理程序。一些例子:
$("a").die('mouseover'); //移除<a>元素上的mouseover事件的所有实时处理程序 $("a").die("mouseover",linkHandler); //移除一个指定的实时处理程序
undelegate()类似die(),但更显式地分开context(内部事件处理程序所注册的元素)和选择器字符串。上面的die()的调用可以写成下面这样
$(document).undelegate('a'); //移除<a>元素上面的所有实时处理程序 $(document).undelegate('a','mouseover'); //移除mouseover的实时处理程序 $(document).undelegate('a','mouseover',linkHandler); //移除指定处理程序
最后,undelegate()也可以不带任何参数调用。在这种情况下,它会注销从选中元素委托的所有实时事件处理程序。
5.动画效果
第14章展示了如何通过脚本来修改文档的css样式。例如,通过设置css的visibility属性,可以显示和隐藏元素。14.3.i节进一步演示了如何通过脚本控制css来产生动画视觉效果。例如,除了仅让一个元素消失,还可以在半秒内逐步减少opacity属性的值,使其快速淡出,而不是瞬间消失。这些动画视觉效果能给用户带来更愉悦的体验,jQuery使其实现起来更简单。
jQuery定义了fadeIn()和fadeOut()等简单方法来实现常见的视觉效果。除了简单的动画方法,jQuery还定义了一个animate()方法,用来实现更复杂的自定义动画。下面将讲述这些简单动画方法,以及更通用的animate()方法。首先,让我们了解下jQuery动画框架的一些通用 特性。
每段动画都有时长,用来指定动画效果执行多长时间。可以使用毫秒数值或字符串来指定时长。字符串"fast"表示200ms。字符串"slow"表示600ms。如果指定的字符串时长jQuery无法识别,则采用默认时长400ms。可以给jQuery.fx.speeds添加新的字符串到数值映射关系来定义新的时长名字:
jQuery.fx.speeds["medium-fast"] = 300;
jQuery.fx.speeds["medium-slow"] = 500;
jQuery动画方法经常使用动画时长来作为可选的第一个参数。如果省略时长参数,通常会得到默认值400ms。注意,省略时长时,有部分方法会立刻跳到最后一帧,没有中间的动画效果:
$("#message").fadeIn(); //用淡入的效果显示元素,持续400ms $("#message").fadeOut("fast");//淡出的效果隐藏元素,持续200ms
禁用动画
在很多网站上,动画视觉效果已经成为标配,但是,并不是所有用户都喜欢:有些用户觉得动画分散注意力,有些则感觉动画导致操作不便,残障用户可能会发现动画会妨碍屏幕阅读器等辅助软件正常工作,老旧硬件用户可能觉得耗费很多cpu时间。为了对用户保持村长,我们通常应该让动画朴素简单,并提供一个选项可以彻底禁用动画。使用jQuery可以非常简单的全局禁用所有动画:简单的设置jQuery.fx.off为true即可。该设置会将每段动画的时长都变成0ms.这样动画就行是没有动画的立刻切换了。
为了最终用户可以禁用动画,可以在脚本上使用如下代码:
$(".shoping").click(function(){jQuery.fx.off = true;});
这样,当网页设计者在页面中加入带有"shoping"类的元素时,用户就可以单击该元素来禁用动画。
jQuery动画是异步的。调用fadeIn()等动画方法时 ,它会立刻返回,动画在“后台”执行。由于动画方法会在动画完成之前返回,因此可以向很多jQuery动画方法传入第二个参数(也是可选的),该参数是第一个函数,会在动画完成时调用。该函数在调用时不会有任何参数传入,但this值会设置为发生动画的文档元素。对于每个选中元素都会调用一次该回调函数:
//用淡入效果快速显示元素,动画完成时,在元素里显示一些文字 $("#message").fadeIn("fast",function(){ $(this).text("Hello world"); });
给动画传入回调函数,可以在动画结束时执行操作。不过,如果只是想顺序执行多段动画的话,回调方式是没有必要的。jQuery动画默认是队列话的(5.ii节下面的“动画选项对象”节会讲述如何覆盖默认方式)。如果一个元素已经在动画过程中,再调用一个动画方法时,新动画不会立刻执行,而会延迟到当前动画结束后才执行。例如,可以让一个元素在持久显示前,先闪烁一阵:
$("#message").fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn();
jQuery动画方法可以接受可选的时长和回调参数。还可以传入一个对象来调用动画方法,该对象的属性指定动画选项:
// 将时长和对调参数作为对象的属性而不是参数传入 $("#message").fadeIn({ duration:"fast", complete:function(){ $(this).text("Hello World"); } });
使用通用的animate()方法时,经常传入选项对象作为参数,其实,这也可以用于更简单的动画方法。使用选项对象可以设置高级选项,比如控制动画的队列和缓动。5.ii节下面的“动画选项对象”节会讲述可用的选项。
i.简单动画
jQuery定义了9个简单动画方法用来隐藏和显示元素。根据实现的动画类型,它们可以分为三组:
fadeIn(),fadeOut(),fadeTo()
这是最简单的动画,fadeIn()和fadeOut()简单改变CSS的opacity属性来显示或隐藏元素。两者都接受可选的时长和回调参数。fadeTo()稍有不同:它需要传入一个opacity的目标值,fadeTo()会将元素的当前opacity值变化到目标值。调用fadeTo()方法是,第一个参数必须是时长(或选项对象),第二参数是opacity目标值,回调函数则是可选的第三个参数。
show()、hide()、toggle()
上面的fadeOut()方法可以让元素不可见,但依旧保留了元素在文档布局中的占位。hide()方法将元素从布局中移除,就好像把CSS的display属性设置为none一样。当不带参数调用时,hide()和show()方法只是简单地隐藏或显示选中元素。带有时长(或选项对象)参数时,他们会让隐藏或显示有个动画过程。hide()在将元素的opacity减少到0时,同时它还将元素的宽度和高度收缩到0,show()这进行反向操作。
toggle()可以改变在上面调用它的元素的可视状态:如果隐藏,则调用show();如果显示,则调用hide()。与show()和hide()一样,必须传入时长或选项对象给toggle()来产生动画效果。给toggle()来产生动画效果。给toggle()传入true和不带参数调用show()是一样的,传入false则和不带参数调用的hide()是一样的。注意,如果传入两个或多个参数给toggle(),它会注册为事件处理程序。这在4.i小节讲述过。
slideDown()、sildeUp()、slideToggle()
slideUp()会隐藏jQuery对象中的元素,方式是将高度动态变化到0,然后设置CSS的display的属性为"none"。slideDown()执行反向操作,来是的隐藏的元素再次可见。slideToggle()使用向上滑动或向下滑动动画来切换元素的可见性。这三个方法都接受可选的时长和回调参数(或选项对象参数)。
下面是一个例子,它调用了改组方法中的每一个。要记得jQuery动画默认情况下是队列化的,依次这些动画会一个接一个的执行:
//用淡出效果将所有图像隐藏,然后显示他们,接着向上滑动,在向下滑动 $("img").fadeOut().show(300).slideUp().slideToggle();
各种jQuery插件(参加第9节)会添加额外的动画方法到jQuery类库中。jQuery UI类库(参见10节)拥有一套特别全面的动画效果。
ii.自定义动画
与简单动画方法实现的效果相比,使用animate()方法可以实现更多通用动画效果。传给animate()方法的第一个参数指定动画内容,剩余参数指定如何定制动画。第一个参数是必须的:它是一个对象,该对象的属性指定要变化的CSS属性和他们的目标值。animate()方法会将每个元素的这些CSS属性从初始值变化到指定的目标值。例如上面描述的slideUp()效果可以用以下代码来实现:
//将所有的图片高度缩小到0 $("img").animate({height:0})
第二个参数是可选的,可以传入一个现象对象给animate()方法:
$("#message").animate({ opacity:0.25, fontSize:10 },{ duration:500, //动画持续半秒 complete:function(){ //动画完成后执行该函数 console.log("end!"); } });
除了让选项对象作为第二个参数传入,animate()方法还允许将三个最常用的选项作为参数传入。可以将动画时长(数值或字符串)作为第二个参数传入。可以指定缓动函数作为第三个参数。(很快就会讲解缓动函数)最后可以将回调函数指定为第三个参数。
通常,animate()方法接受两个对象参数。第一个指定动画内容,第二个指定如何定制动画。要彻底理解如何使用jQuery来实现动画,还需要弄明白这两个对象的更多细节。
⑴动画属性对象
animate()方法的第一个参数必须是对象。该对象的属性名必须是CSS属性名,这些属性的值必须是动画的目标值。动画只支持数值属性:对于颜色,字体或display等枚举属性是无法实现动画效果的(jQuery的实现方式不支持非数值动画,但有其它实现方案,比如传入自定义的CSS属性变化函数)。如果属性是数值,则默认单位是像素。如果属性值是字符串,可以指定单位,如果省略单位,则默认依旧是像素。还可以指定相对值,用"+="前缀表示增加,或用"-="表示减少。例如:
$("#message").animate({ "margin-left": "+=40", "opacity":"-=.8" });
注意上面的对象字面量中属性名“margin-left”两旁引号的使用。属性名中的连字符表示不是一个合法的javascript标识符,所以必须用引号括起来 ,当然jQuery也允许使用marginLeft这种大小写混合的写法。
除了数值(可以带有单位、“+=”或"-="前缀),在jQuery动画属性对象中可以使用其他三个值,“hide”值会保存属性的当前值,然后将属性值变化到0。“show”值会将css属性的值还元到之前保存的值,jQuery会在动画完成时调用show()方法。如果一段动画使用了"hide",jQuery会在动画完成时调用hide()方法。
还可以使用"toggle"来实现显示和隐藏,具体效果取决于该属性的当前设置。可以用下面的代码实现"slideRight"效果(和slideup()方法类似,只是动画内容是元素宽度):
$("img").animate({ width:"hide", borderLeft:"hide", borderRight:"hide", paddingLeft:"hide", paddingRight:"hide" })
将上面的属性值替换为"show"或“toggle”,就可以产生水平滑动的伸缩效果。类似slideDown()和slideToggle()效果。
⑵动画选项对象
animate()方法的第二个参数是可选的,该选项对象来指定动画如何执行。有两个最重要的选项我们已经接触过,duration属性指定动画效果的持续时间,该属性的值还可以是"fast"、“slow”或任何在jQuery.fx.speeds中定义名称
另外一个接触过的选项是complete属性:它指明在动画完成时的回调函数。和complete属性类似,step属性指定在动画每一步 或每一帧调用的回调函数。在回调函数中,this指向正在连续变化的元素,第一个参数则是正在变化的属性的当前值。
在选项对象中,queue属性指定动画是否需要队列化——是否需要等到所有尚未发生的动画都完成后再执行该动画。在默认的情况下,所有的动画都是队列化的。将queue属性设置为false可以取消队列化。非队列化的动画会立刻执行。随后队列化的动画不会等待费队列化的动画执行完成之后才执行。考虑如下代码:
$("img").fadeIn(500) .animate({"width":"+=100"},{queue:false,duration:1000}) .fadeOut(500);
上面的fadeIn()和fadeOut()效果是队列化的,但animate()的调用(在1000毫秒内改变width属性)是非队列化的。这段width动画和fadeIn()效果的开始时间相同。fadeOut()效果会在fadeIn()动画效果完成时开始,它不会等到width动画完成。
缓动函数
实现动画时,时间和动画属性值之间可以是线性关系,这些方式很直接,但不够好。例如,一段时长400ms的动画,在100ms时,动画完成了25% 。在该线性动画中,如果将不透明度从1.0变化到0,(可能是一个fadeOut()调用),则在100ms时,不透明度为0.75,然而,事实表明,非线性的动画效果会带来更愉悦的体验。因此,jQuery引入了“缓动函数”,来将基于实际的0-1之间的值。缓动函数会返回一个0~1之间的值。通常,缓动函数传入0时返回0,在传入1时返回1。当然,在0-1之间缓动函数可以是非线性的,这可以让动画有加速和减速效果。
jQuery默认的缓动函数是正弦函数:它开始很慢,接着加速,然后再缓慢“缓动”变化到终值。jQuery中的缓动函数有名字。默认的缓动函数名字为“swing”,jQuery还实现了一个线性的缓动函数,名字为"linear"。可以添加自定义缓动函数到jQuery.esaing对象上。
jQuery.easing["squareroot"] = Math.sqrt;
在jQuery UI类库中,有一个“jQuery缓动插件”定义了一套更复杂的缓动函数。
剩余的动画选项和缓动函数有关。选项对象的easing属性指定缓动函数名。jQuery默认使用名为“swing”的正弦函数。如果想让动画线性变化,可以使用如下选项:
$("img").animate({"width":"+=400"},{duration:500,easing:"linear"});
除了传入选项对象,duration、easing和complete选项可以指定为animate()方法的参数。因此,上面的代码还可以简写成。
$("img").animate({"width":"+=100"},500,"linear")
最后,jQuery动画框架甚至还允许为不同的css动画属性指定不同的缓动函数。这有两种方式来实现,代码实例如下:
//用hide()方法隐藏图片,图片的大小采用线性动画 //不透明则使用默认的"swing"缓动函数 //实现方式一:使用specialEasing选项来指定自定义缓动函数 $("img").animate({width:"hide",height:"hide",opacity:"hide"}, {specialEasing:{width:"linear",height:"linear"}}); //实现方式二:在第一个对象中传入[目标值,缓动函数]数组 $("img").animate({ width:["hide","linear"],height:["hide","linear"],opacity:"hide" });
iii.动画的取消、延迟和队列
jQuery还定义了一些动画和队列相关的方法,我们需要进一步了解。首先是stop()方法,它用来停止选中元素上的当前正在执行的任何动画。top()方法接受两个可选的布尔值参数。如果第一个参数是true,会清楚该选中元素上的动画队列:除了停止当前动画,还会取消任何等待执行的动画。第一个参数的默认值是false:如果忽略该参数,等待执行的动画不会被取消。第二个参数用来指定正在连续变化的CSS属性是否保留当前值,还是应该变化到最终目标值。传入true可以让他们变化到最终值。传入false(或省略该参数)会让他们保持为当前值
当动画是由用户事件触发时,在开始新的动画前,可能需要取消掉当前或等待执行的任何动画,比如
$("img").bind({ mouseover:function(){ $(this).stop().fadeTo(300,1.0); }, mouseout:function(){ $(this).stop().fadeTo(300,0.5) } });
与动画相关的第二个方法是delay()。这会直接添加一个事件延迟到动画队列中:第一个参数是时长(毫秒为单位的数值或字符串),第二个参数是队列名,是可选的(通常并不需要第二个参数:接下来我们会解释队列名)。可以在符合动画中使用delay(),代码如下:
// 快速淡出为半透明,等一等,然后向上滑动 $("img").fadeTo(100,0.5).delay(200).slideUp()
在上面的stop()方法例子中,使用mouseover和mouseout事件来变化图片的透明度。可以调整该例子:在开始动画时,添加一个短小的延迟。这样,当鼠标快速滑过图片而不停留时,不会有任何分神的动画产生:
$("img").bind({ mouseover:function(){ $(this).stop(true).delay(100).fadeTo(300,1.0); }, mouseout:function(){ $(this).stop(true).fadeTo(300,0.5) } });
和动画相关的最后一组方法可以对jQuery的队列机制进行底层操作。jQuery队列是按照顺序执行的函数列表。每一个队列都与一个文档元素(或者是Document或Window对象)关联,每一个元素的队列都与其它元素的队列彼此独立。可以使用queque()方法给队列添加一个新函数。当某个函数到达队列头部时,它会自动从队列中去除并调用。当函数被调用时,this指向与队列相关的元素。被调用的函数会传入唯一一个回调函数作为参数。当函数完成运行时,它必须调用回调函数。这可以运行队列中的下一个操作,如果不调用回调函数,该队列会停止运行,剩余的函数将永远不会被调用。
我们知道,通过给jQuery动画方法传入回调函数,就可以在动画完成后执行一些操作。通过对函数执行队列操作,也可达到这一目的:
//淡入显示一个元素,稍等片刻,设置一些文字,然后变化边框 $("#message").fadeIn().delay(800).queue(function(next){ $(this).text("Hello world"); //显示一些文字 next();//运行队列中的下一项 }).animate({"margin-top":"+=40"});
队列函数中的回调函数参数是jQuery 1.4引入的新特性。对于jQuery类库之前的版本,需要调用dequeque()方法“手动”取消队列中的下一个函数:
$(this).dequeue(); //替代next()方法
如果在队列中什么也没有,调用dequeue()方法不会有任何响应。反之,它则会将队列头部的函数从队列中移除,并调用它。设置的this值和传入的回调函数如上所述。
还有一些笨拙的方式来操作队列,clearQueue()方法来清除队列。给queue()方法传入一个函数组成的数组而不是单一函数时,会用传入的函数数组来替换当前队列。如果在调用queue()方法时,不传入任何参数,则会返回当前队列数组。jQuery还将queue()和dequeue()定义成工具函数。如果想给元素e的队列添加一个工具函数f,可以使用以下方法或函数:
$(e).queue(f); //创建一个特有的e的jQuery对象,并调用queue()方法。 jQuery.queue(e,f) //直接调用jQuery.queue()工具函数
最后,特意留下queue()、dequeue()和clearQueue()方法都可以有一个可选的队列名来作为第一个参数。jQuery动画方法使用的队列名是"fx",这是没有指定队列名时默认使用的队列。当想要顺序执行异步操作时,jQuery队列机制非常有用:原来需要给一个异步操作传入回调函数来触发队列中的下一个函数,现在可以直接使用 jQuery队列来管理异步序列。只需传入非"fx"的队列名,并记得队列中的函数不会自动执行。必须显式调用dequeue()方法来运行第一个函数,然后每一步操作在完成时必须把下一个操作从队列中移除。
6.jQuery中的ajax
在Web应用编程技术里,Ajax很流行,它使用HTTP脚本(参考16章)来按需加载数据,而不需要加载整个页面。在现在的web应用中,Ajax技术非常有用,因此,jQuery内置了Ajax工具来简化使用。jQuery定义了一个高级工具方法和四个高级工具函数。这些高级工具都基于一个强大的底层函数:jQuery.ajax()。下面的章节会首先描述这些高级工具,然后然后再详细阐述jQuery.ajax()函数。为了彻底的理解高级工具的使用,我们需要理解jQuery.ajax(),即便可能永远不显式的使用它。
1.load()方法
load()是所有jQuery.ajax()工具中最简单的,向它传入一个URL的内容,然后将内容插入每一个选中元素中,替换掉已经存在的内容。例如:
//每个60秒加载并显示最新的状态报告 setInterval(function(){ $("#status").load("ubd.html"); },60000)
4.i也讲到了load()方法,它用来注册load事件的处理程序。如果传给改方法的第一个参数是函数而不是字符串,则load()方法是事件处理程序注册方法而不是Ajax方法。
如果只想显示被加载文档的一部分,可以在URL后面添加一个空格和一个jQuery选择器。当URL加载完成后,jQuery会用指定的选择器来从加载好的HTML中选取需要显示的部分:
//加载并显示天气遇到的温度部分 $("temp").load("wheather_report.html #temperature");
注意:URL后面的选择器看起来很像片段标识符(url的hash部分)。不同的是,如果想只插入被加载的文档的选中部分的话,则空格是必须的。
除了必须的URL参数,load()方法还接受两个可选参数。第一个可选参数表示的数据,可以追加到URL后面,或者与请求一起发送。如果传入的是字符串,则会追加到URL后面(放在“?”或"&"的后面)。如果传入对象,该对象会被转化为用"&"分隔的name/value对与请求后一起发送。(对象转化为字符串的具体细节在6.2节下面的(2).jQuery.getJSON()节中描述)通常情况下,load()方法发送HTTP GET请求,但是如果传入数据对象,则它会发送POST请求。下面是两个例子。
// 加载特定区号的天气预报 $("#temp").load("us_weather_report.html", "zipcode=02134") // 使用对象作为数据,并指导为华氏温度 $("#temp").load("us_weather_report.html",{zipcode:02134,units:"f"})
load()方法的另一个可选参数是回调函数。当ajax请求成功或未成功,以及(当请求成功时)URL加载完毕并插入选中元素时,会调用该回调函数。如果没有指定任何数据,回调函数可以作为第二个参数传入。否则它必须是第三个参数。在jQuery对象的每一个元素上都会调用回调函数,并且每次调用都会传入三个参数:被加载的URL的完整文本内容、状态码字符串,以及用来加载该URL的XMLHTTPRequest对象。其中,状态参数是jQuery的状态码,不是HTTP的状态码,尤其是类似“succes”、“error”和“timeout”的字符串。
jQuery的Ajax状态码
jQuery的所有Ajax工具,包括load()方法,会调用回调函数来提供请求成功或失败的异步消息。这些回调函数的第二个参数是一个字符串,可以取以下值:
"success"
表示请求成功完成
"notmodified"
该状态码表示请求已经正常完成,但服务器返回的响应内容是HTTP 304“Not Modified”,表示请求的URL和上次请求的相同,没有变化。只有在选项中设置ifModified为true时,该状态码才会出现(参考6.iii节下面的“(1)通用选项”节),从jQuery1.4版本开始,认为“notmodified”状态码是成功的,但之前的版本会将其当成错误。
"error"
表示请求没有成功完成,原因是某些HTTP错误。更多细节,可以检测传入每一个回调函数中的XMLHTTPRequest对象的HTTP状态码来获取。
"timout"
如果Ajax请求没有在选定的超时区间内完成,会调用错误回调,并传入该状态码。默认情况下,jQuery的Ajax请求没有超时限定,只有指定了timeout选项(见6.iii节下面的“1.通用选项”节时才能看到改状态码)。
"parsererror"
该状态码表示HTTP请求已经成功完成,但jQuery无法按照期望的方式解析。例如,如果服务器返回不符合格式的XML文档或不符合格式的JSON文本时,就会出现该状态码,注意拼写:“parsererror”,而不是"parseerror"
ii.Ajax工具函数
jQuery的其它Ajax高级工具不是方法,而是函数,可以通过jQuery或$()指教调用,而不是在jQuery对象上调用。jQuery.getScript()加载并执行javascript代码文件。jQuery.getJSON()加载URL,并将其解析为JSON,并将解析结果传递到指定的回调函数中。这两个函数都会调用一个更通用URL获取函数:jQuery.get()。最后,jQuery.post()和jQuery.get()很类似,除了执行的是HTTP POST而不是GET请求。与load()方法一样,所有这些函数都是异步的:在任何数据加载钱他们就会返回调用者,加载结果则通过调用指定的回调函数来通知。
⑴.jQuery.getScript()
jQuery.getScript()函数的第一个参数是javascript代码文件的URL。他会异步加载文件,在加载完成后再全局作用域执行该代码。它能同时适用于同源和跨源脚本:
//从其它服务器动态加载脚本 jQuery.getScript("http://baidu.com/a.js");
也可以传入回调函数作为第二个参数,在这种情况下,jQuery会在代码假值和执行完成后调用一次该回调函数。
//加载一个类库,并在加载完成完成时立刻使用它 jQuery.getScript("js/mu.plus.js",function(){ $("div").my_plugin(); //使用加载的类库 });
jQuery.getScript()通常会使用XMLHttpRequest对象来获取要执行的脚本内容。但对于跨域请求(脚本与当前文本不在一样的服务器上),jQuery会使用<script>元素来加载脚本。(参考14.2章小节)。在同源的情况下,jQuery.getScript()函数的返回值也是该XMLHTTPRequest对象。对于跨域请求,不存在XMLHTTPRequest对象,并且返回脚本的内容取不到。在这种情况下,回调函数的第一个和第三个参数是undefined,jQuery.getScript()的返回值也是undefined。
传递给jQuery.getScript()的回调函数,仅在请求成功完成时才会被调用。如果需要在发生错误已经成功时都得到通知,则需要使用底层的jQuery.ajax()函数。该节所描述的其他三个工具函数也是如此。
⑵.jQuery.getJSON()
jQuery.getJSON()与jQuery.getScript类似:它会获取文本,然后特殊处理一下,再调用指定的回调函数。jQuery.getJSON()取到文本后,不会将其当做脚本执行,而会将其解析为JSON(第7节会描述jQuery.parseJSON()函数)。jQuery.getJSON()只有在传入回调函数时才有用。当成功加载URL,以及将内容解析成为JSON后,解析结果会作为第一个参数传入回调函数中。与jQuery.getScript()一样,回调函数的第二个和第三个参数是"success",状态码和XMLHttpRequest对象:
//假设 data.json 包含文本:{"x":1,"y":2} jQuery.getJSON("data.json", function(data) { //data 参数是对象{ x:1,y:2 } });
与jQuery.getScript()不同,jQuery.getJSON()接受一个可选的数据对象参数,就和传入load()方法中的一样。如果传入数据到jQuery.getJSON()中,该数据必须是第二个参数,回调函数则是第三个。如果不传入任何数据,则回调函数可以是第二个参数。如果数据是字符串,则它会被添加到URL的"?"或"&"后面。如果数据是一个对象,则它会转化为字符串。(参加下面内容),然后添加到URL上
传递数据给jQuery的Ajax工具
jQuery的大多数Ajax方法都接受一个参数(或选项)用来指定与URL一起发送给服务器数据,通常,该数据的形式是URL编码,用“&”分隔的名/值对。(这个数据格式就是已知的"application/x-www-form-urlencoded" MIME类型。这类似JSON格式:一种将javascript简单对象与字符串互相转化的格式。)对于HTTP GET请求,该数据字符串会添加到请求 URL后面。对于POST请求,则在所有发送的HTTP请求头后面,当做请求的内容体来发送它。
获取该格式的数据字符串的一种方式是,调用包含表单或表单元素的jQuery对象的serialize()方法。例如,可以使用如下代码来调用load()方法提交HTML表单:
$("#submit_button").click(function(event) { $(this.form).load( //通过加载新内容来替换表单 this.form.action, //表单url $(this.form).serialize() //将表单数据附加到URL后面 ); event.preventDefault(); //取消表单的默认提交; this.disabled = "disabled"; //防止多次提交 });
如果将jQuery ajax函数的数据参数(或选项)设置为对象不是字符串,jQuery通常会调用jQuery.param()将对象转化成字符串,jQuery通常会调用jQuery.param()将对象转化成字符串(除了下面提到的一个异常)。该工具函数会将对象的属性当成名/值对,例如,会将对象{x:1,y:"hello"}转化为字符串"x=1&y=hello"。
如果将jQuery ajax函数的数据参数(或选项)设置为对象不是字符串,jQuery通常会调用jQuery.param()将对象转化成字符串,jQuery通常会调用jQuery.param()将对象转化成字符串(除了下面提到的一个异常)。该工具函数会将对象的属性当成名/值对,例如,会将对象{x:1,y:"hello"}转化为字符串"x=1&y=hello"。
如果传递给jQuery.getJSON()的URL或数据字符串在末尾或“&”字符前含有"=?"字符串, 则表明这是一个JSONP请求。jQuery会创建一个回调函数,并用该回调函数的函数名替换掉"=?"中的“?”号,接着jQuery.getJSON()的行为就会想请求脚本文件一样,而不是JSON对象。这对金泰的JSON数据文件无效,它只能与支持JSONP的服务器脚本一起才能工作。由于JSONP被当做脚本来处理,这意味着JSON格式的数据可以跨域请求。
⑶.jQuery.get()和jQuery.post()
jQuery.get()和jQuery.post()获取指定URL的内容,如果有数据的话,还可以传入指定数据,最后则将结果传递给指定的回调函数。jQuery.get()使用HTTP GET请求来实现,jQuery.post()使用HTTP POST请求,其它两者则是一样的。与jQuery.getJSON()一样,这两个方法也是接受相同的三个参数:必需的URL,可选的字符串或对象,以及一个技术上可选但实际上总会使用的回调函数。调用的回调函数会传入三个参数:第一个参数是返回的数据;第二个是“success”;第三个则是XMLHttpRequest对象(如果有的话):
// 从服务器上请求文件并在警告对话框中显示 jQuery.get("ajax/debug.txt",alert);
除了上面描述的三个参数,还有两个方法接受可选的第4个参数(如果省略数据参数的话,则作为第三个参数传入),该参数指定被请求数据的类型。第4个参数会影响在传入回调函数前数据的处理。load()方法使用"HTML"类型,jQuery.getScript()使用“script”类型,jQuery.getJSON()则使用"json"类型。与上面这些专用的函数相比。jQuery.get()和jQuery.post()更灵活。该参数的有效值,以及省略该参数时jQuery的行为,下面描述。
jQuery的Ajax数据类型
可以给jQuery.get()或jQuery.post()传递下面6种类型作为参数。此外,下面会讲到,使用dataType选项也可以传递这些类型给jQuery.ajax()方法:
"text"
将服务器的响应作为纯文本返回,不做处理
"html"
类型和"text"一样:响应是纯文本。load()方法使用该类型,将返回的文本插入到文档目录自身中。
"xml"
请求的URL被认为指向XML格式的数据,jQuery使用XMLHttpRequest对象的responseXML属性来替代reponseText属性。传给回调函数的值是一个表示该XML文档的Doccument对象,而不是保存文档文本的字符串。
"script"
请求的URL被认为指向javascript文件,返回的文本在传入回调函数钱,会当做脚本执行。jQuery.getScript()使用该类型。当类型是“script”时,jQuery可以使用script元素来替代XMLHttpRequest对象,因此用来处理跨域请求。
"json"
请求的url被认为指向json格式的数据文件。会使用jQuery.parseJSON()(参考7节)来解析返回的内容,得到JSON对象后传入回调函数。jQuery.getJSON()使用该类型。如果类型是"json"同时URL或数据字符串含有“=?”会被转换成jsonp。
"jsonp"
请求的url被认为指向服务器脚本,该脚本支持jsonp协议,可以将json格式的数据作为参数传递给客户端指定的函数。(jsonp的更多的细节请参考16.2节)在该类型下,传递给回调函数的是解析好的对象。由于jsonp请求可以通过script元素来实现,因此该类型可以用来跨域请求,就和使用script类型一样。使用该类型时,url或数据字符串经常会包含一个类似"&jsonp=?"或"&callback=?"的参数。jQuery会将其中的"?"替换为自动产生的回调函数名。(可参考6.3,(3)不常用的选项和钩子节中的jsonp和jsonpCallback选项来替代)
如果调用jQuery.get()、jQuery.post()或jQuery.ajax()函数时没有指定以上类型中的任何一个,jQuery会检查HTTP响应中的Content-Type头。如果该头部信息包含“XML”字符串,则传入回调函数中的是xml文档。否则,如果头部包含"json"字符串,则数据被解析成JSON并把解析后的对象传递给回调函数。否则,如果头部含有javascript字符串,则数据被当成脚本执行。如果以上都不符合,则数据会被当成纯文本执行。
3.jQuery.ajax()函数
jQuery的所有的Ajax工具都会调用jQuery.ajax()————这是这个库中最复杂的函数。jQuery.ajax()仅接受一个参数:一个选项对象,该对象的属性指定ajax请求如何执行的很多细节。例如:jQuery.getScript(url,callback)与以下jQuery.ajax()调用等价:
jQuery.ajax({ type:"get",//HTTP请求方法 url:url, //要获取数据的地址 data:null, //不要给url添加任何数据 dataType:"script", //一旦获取到数据,立即执行当前脚本 success:callback //完成时调用的函数 });
jQuery.get()和jQuery.post()也接受上面这5个基本选项。然而,如果直接调用jQuery.ajax()的话,它可以支持更多的选项。下面会解释所有选项(包含上面5个基本选项。)
在深入了解所有选项之前,我们得知道可以通过给jQuery.ajaxSetup()传入一个详细对象来设置任意默认值:
jQuery.ajaxSetup({ timeout:2000, //在2秒后取消所有ajax请求 cache:false //通过给url添加事件戳来禁用浏览器缓存 });
运行以上代码后,指定的timeout和cache选项会在所有未指定这两个选项的值的Ajax请求中使用,包括jQuery.get()和load()等高级工具。
在阅读下面章节中,jQuery的大量选项和回调函数时,参考6.i和6.ii节下面的"(3).jQuery.get()和jQuey.post()"节中关于jQuery Ajax状态码和数据类型字符串的内容非常有助于理解。
(1).通用选项
jQuery.ajax()中常用的选项如下:
type
指定http的请求方法。默认是"GET".另一个常用值是"POST".可以指定其它HTTP方法,比如"DELETE"或"PUSH",但不是所有的浏览器都支持它们。
url
要获取的url.对于GET请求,data选项会添加到该URL后。对于JSONP请求,当cache选项为false时,jQuery可以添加参数到URL中。
data
添加到URL中(对于GET请求)或请求在主体内容中(对POST请求)发送的数据。这可以是字符串也可以是对象。通常会把对象转化为字符串,就如6.ii节下面的(2)jQuery.getJSON()节中描述的一样,除了在process data选项中描述的异常情况。
dataType
指定响应数据的预期类型,以及jQuery处理该数据的方式。合法的值是"text" "html" "script" "json" "jsonp" "xml"。6.ii下面的(3)jQuery.get()和jQuery.post()节有解释这些值的含义。选项没有默认值。当没有指定时,jQuery会检测响应中的Content-Type头来确定如何处理返回的数据。
contentType
指定请求的HTTP Content-Type头。默认是"application/x-www-form-urlencoded",这是HTML表单和绝大部分服务器脚本使用的正常值。如果将type选项设置为"POST",想发送纯文本或XML文档作为请求体时,需要设该值。
timeout
超时时间,当请求没有在指定的时间内完成时,请求会取消同时触发error回调,回调中心的状态码参数为"timeout"。默认超时时间为0,表示除非请求完成,否则永远不取消。
cache
对于GET请求,如果该选项设置为fasle,jQuery会添加一个“_=”参数到URL中,或者替换已经存在的同名参数。该参数的值是当前时间(毫秒格式)。这可以禁用浏览器缓存,因为每次请求的URL都不一样
ifModified(此处省略)
global
该选项指定jQuery是否应该触发上面描述的Ajax请求过程中的事件。默认是true;设置改选项为false会禁用ajax相关所有事件。(参考6.iiii获取事件的细节)。
⑵回调
下面的选项指定在Ajax请求的不同阶段调用的函数。success选项已经很熟悉了:则是传入给jQuery.getJSON()等方法的回调函数。注意jQuery也会将AJax请求过程的消息当做事件发送(除非设置了global选项为false)。
context
该选项指定回调函数在调用时的上下文对象————就是this。该选项没有默认值,如果不设置,this会指向选项对象。设置context选项也会影响Ajax事件触发的方式(参考6.iiii节)。如果设置该选项,值应该为window、Document或触发事件所在的Element。
beforeSend
该选项指定Ajax请求发送到服务器之前激活的回调函数。第一个参数是XMLHttpRequest对象,第二个参数是该请求的选项对象。beforeSend回调使得程序有机会在XMLHTTPRequest对象上设置自定义HTTP头部。如果改回调函数返回false,Ajax请求会取消。注意跨域的的script和jsonp请求没有使用XMLHttpRequest对象,因此不会触发beforeSend回调。
succes
该选项指定Ajax请求成功完成时调用的回调函数。第一个参数是服务器发送的数据;第二个参数是jQuery状态码;第三个是用来发送请求的XMLHTTPRequest对象。6.ii下面的(3)jQury.get()和jQuery.post()节所描述,第一个参数的类型取决于dataType选项服务器响应的Content-Type头信息。如果类型是“XML”,则第一个参数是Document对象。如果类型是json或jsonp,第一个参数是返回服务器的JSON格式响应的解析结果。如果类型是script,则响应内容是所加载脚本的文本内容(该脚本已经执行了,因此在这种情况下可以忽略响应内容。)对于其他类型,响应内容直接就是请求资源的文本内容。
第二个参数的状态码通常是字符串"succes",如果设置了ifModified选项,该参数就可能是"notmodified"。这种情况下,服务器不发送响应并且不顶用第1个参数。“script”和"jsonp"类型的跨域通过请求<script>元素而不是XMLHttpRequest执行,对于那些请求,不会定义第三个参数。
error
该选项指定Ajax请求不成功时的回调函数。该回调的第1个参数是该请求的XMLHTTPRequest对象(如果可能用到的话)。第二个参数是JQuery的状态码。对于HTTP错误,该状态码可能是“Error”,对于超时,则是"timeout",“parsererror”则表示解析服务器响应时出了问题。例如:xml文档或json对象不符合格式,则状态码为"parsererror"。这种情况下,error回调的第三个参数则是抛出的Error对象。注意dataType为“script”的请求在返回无效的javascript代码时不会触发错误。脚本的任何错误都会忽略掉,调用的回调则是success而不是error。
complete
该对象指定ajax请求完成时激活的回调函数。每一个ajax请求或者成功调用success回调。当失败时调用error回调。在调用success或error后,jQuery会调用complete回调。传给complete回调的第一个参数是XMLHTTPRequest对象,第二个参数则是状态码。
⑶.不常用的选项和钩子
下面的ajax选项不经常使用。某些特定的选项通常不可能设置。令一些选项则提供了自定义的钩子,是的可以修改jQuery Ajax请求的默认处理方式
async
脚本化化的HTTP请求本身就是异步的。然而,XMLHTTPRequest对象提供了一个选项,可以阻塞当前进程,直到接手到响应。如果想开启这一阻塞行为,设置该选项为false。设置该选项不会更改jQuery.ajax()的返回值:如果有使用XMLHTTPRequest对象的话,该函数会始终返回该对象。对于同步请求,可以从XMLHTTPRequest对象中提取服务器的响应和HTTP状态码,如果想要获取jQuery解析的响应和状态码,可以指定一个complete回调(就和给异步请求指定的一样)。
dataFilter
该选项指定一个函数,用来过滤或预处理服务器返回的数据。第一个参数是从服务器返回的原始数据(字符串或XML请求返回的Document对象),第二个参数是dataType的详细的值。如果指定该函数,则它必须返回一个值,该值会来替换掉服务器的响应。注意dataFilter()函数会在JSON解析和脚本执行前执行。同时注意跨域的script和jsonp请求不会调用dataFilter().
jsonp
当设置dataTpye选项为"jsonp"时,url或data选项通常会包含一个类似"jsonp=?"参数。如果jQuery在URL或data选项中没有找到类似参数时,会使用该选项指定的名字插入一个。该选项的默认值是callback.在使用jsonp时,如果服务器需要一个不同的参数吗,而url或data选项中又没有指定时,需要设置该选项。
jsonpCallback
对于dataType选项为“jsonp”的请求(或url中带有类似"jsonp=?"这种JSONP参数的“json”请求),jQuery必须将url中的"?"替换成包装函数,服务器会将数据传递给该该包装函数。通常,jQuery会根据当前时间来生成一个唯一的函数名。如果想用自己的函数来替代jQuery生成的,则可以设置该选项。但是,一旦这样做了,会阻止jQuery在触发正常时间时调用success或complete回调。
processData
当设置data选项为对象(或将对象作为第二个参数传递给jQuery.get()和相关方法)时,jQuery通常将对象转换为字符串,该字符串遵循标准的HTML“application/x-www-form-urlencoded”格式(参考6.ii(2)jQuery.getJSON()节)。如果想省略掉该步奏(比如想将Document对象作为POST请求整体发送,)请设置该选项为false。
scriptCharset
对于跨域的"script"和"jsonp"请求,会使用<script>元素,选项用来指定它的元素的charset属性值。该选项对正常的基于XMLHttpRequest请求不会有任何作用。
tranditional
jQuery1.4改变了数据对象序列化为"application/x-www-form-urlencoded"字符串的方式(细节参考6.ii(2)jQuery.getJSON()节)。设置该选项为true,可以让jQuery回复到原来的方式。
usename,password
如果请求需要密码验证,请使用着两个选项来指定用户名和密码
xhr
该选项指定一个工厂函数,用来获取XMLHTTPRequest对象。该工厂函数在调用时不带参数,而且必须返回一个实现了XMLHTTPRequest API对象。这个非常底层的钩子可以创建自己对XMLHTTPRequest的包装,可以给方法添加特性或测量。
iiii.Ajax事件
6.iii节中的“回调”节描述了jQuery.ajax()拥有的4个回调选项:beforeSend、success、error、complete。除了分别激活这些制度的回调函数,jQuery的Ajax函数还会在Ajax请求的每一个相同阶段触发自定义事件。下面的表格展示了这些回调函数响应的事件:
回调 | 事件类型 | 处理程序注册方法 |
beforeSend | "ajaxSend" | ajaxSend() |
sucess | "ajaxSuccess" | ajaxSucess() |
error | "ajaxError" | ajaxError() |
complete | "ajaxComplete" | ajaxComplete() |
"ajaxStart" | ajaxStart() | |
"ajaxStop" | ajaxStop() |
可以使用bind()方法和上表第二列中的事件类型字符串来注册这些自定义Ajax事件,也可以使用第三列中的事件注册方法来注册。ajaxSuccess()和其它方法的使用方式就用click()、mouseover()以及4.i节中中的其它简单事件注册方法一样。
由于Ajax事件是自定义事件,是由jQuery而不是浏览器产生的,因此传递给事件处理程序的Event对象不是很有用。其实,ajaxSend、ajaxSuccess、ajaxError和ajaxComplete事件在触发时都带有其它参数。这些事件的处理程序激活时再event参数后都带有两个额外的参数。第一个额外的参数是XMLHTTPRequest对象,第二个额外参数是选项对象。例如,这意味着ajaxSend事件的处理程序可以向XMLHttpRequest对象添加自定义头,就和beforeSend回调可以做的一样。除了上面描述的两个额外参数,触发ajaxError事件时还会带有第三个额外参数。如果有的话,事件处理程序的第三个额外参数是Error对象,是在发生错误时抛出的。令人奇怪的是,这些Ajax事件并没有传入jQuery的状态码。例如,如果ajaxSuccess事件的一个处理程序需要区分“success”和"notmodified",则它需要从XMLHTTPRequest对象中检测元素HTTP状态码。
上面表格中例举的最后两个事件与其他事件不同,最明显的是他们没有响应的回调函数,同时他们在触发时不带额外参数。ajaxStart和ajaxStop是一对表示与Ajax相关的网络活动开始和停止的事件。当jQuery没有执行任何Ajax请求时,如果开始一个新请求,它就会触发ajaxStart事件。如果在第一个请求还没有完成是,其它请求就开始了。这些新请求不会触发新的ajaxStart事件。这一对事件能很方便地用来显示和隐藏某些“加载中。。。”动画或网络的图标:
$("#loading_animate").bind({ ajaxStart:function () { $(this).show(); }, ajaxStop: function () { $(this).hide(); } });
这些ajaxStart和ajaxStop的事件处理程序可以绑定到任意文档元素上:jQuery是全局的触发它们(4.iiiiii)。其它4个ajax事件,ajaxSend、ajaxSuccess、ajaxError、ajaxComplete、通常也是全局触发的,因此,处理程序可以绑定到任何元素上。然后,如果在调用jQuery.ajax()时设置了context选项,则这4个事件不会全局触发,而会在context元素上触发。
最后,记住可以通过设置global选项为false来组织jQuery触发任何Ajax相关的事件。尽管这个选项名很让人迷惑,但是设置global为false可以让jQuery不再在context对象上触发事件已经不再全局地触发事件。
7.工具函数
jQuery类库定义了不少工具函数(还有两个属性),在编写程序时挺有用。在下面的列表中你会发现,部分函数在ECMAScript中已经有了等价的形式。jQuery的函数比ES5早,并且可以工作在所有浏览器中。按照字母排序,将这些工具函数列举如下:
jQuery.browser
if($.browser.mozilla && parseInt($.browser.version) < 4) { //...在此解决假设的firefox bug }
jQuery.contains()
该函数接受两个文档元素作为参数。如果第一个元素包含第二个元素,则返回ture;否则返回false。
jQuery.each()
和each()方法不同,each()方法只能变量jQuery对象,而jQuery.each()工具函数可以遍历数组元素或对象上调用的函数。第二个参数是要在每个数组元素或对象属性上调用的函数。该函数在调用时会带有两个参数:数组元素的序号或对象的属性名,以及数组元素的值或对象的属性值。函数中的this值和第二个参数是一样的。如果该函数返回false,jQuery.each()会停止当前遍历会立即返回。jQuery.each()总会返回第一个参数的值。
jQuery.each()会使用普通的for/in循环来遍历对象属性,所有会遍历所有可枚举的属性,包含继承的属性。jQuery.each()在遍历数组元素时,会以序号从小到大来遍历,不会跳过稀疏数组中的undefined属性。
jQuery.extend()
该函数接受对象作为参数。它会将第二个以及以后参数对象的属性复制到第一个参数对象中,如果同名的属性已经在第一个参数对象中,则会覆盖它。该函数会忽略任何值为undefined或null的属性。如果仅传入了一个对象,该对象的属性会被复制到jQuery对象自身中。该对象的返回值是属性被复制到的对象。如果一个参数的值为true,则会执行拷贝:第三个(以及以后)对象的属性会被复制到第二个对象上。
该函数用来复制对象以及合并带有几组默认值的选项对象时非常有用:
var clone = jQuery.extend({},original); var options = jQuery.extend({},default_options,user_options);
jQuery.globalEval()
该函数会在全局上下文中执行javascript代码字符串,就像它是<script>的元素内容一样。(实际上,jQuery实现该函数时,就是通过创建一个<script>元素并临时把它插入到文档中来显示的。(新版的jQuery里,已经优化了实现,实现了eval和execScript来执行))。
jQuery.gerp()
该函数和ES5中的Array对象的filter()方法类似。它接受数组作为第一个参数,以及一个判断函数作为第二个参数,该判断函数会在数组的第一个元素上调用,调用时会传入元素值和元素的序号作为参数。jQuery.gerp()返回一个新数组,该数组由调用判断函数时返回true(或其他真值)的元素组成。如果给jQuery.gere()传入true作为第三个参数,则它会反转判断函数,返回的数组将会判断函数调用时为false或其他假值的元素组成。
jQuery.inArray()
该函数和ES5中的Array对象indexOf()方法类似,它的第一个参数可以是任意值,第二个参数则是数组(或类数组对象),返回值是第一个参数值在数组中第一次出现的序列号,如果该参数只不存在的话,则返回-1.
jQuery.isArray()
当参数是原生对象时,返回true.
jQuery.isEmptyObject()
当参数对象没有可枚举的属性是,返回true.
jQuery.isFunction()
当参数是元素Function对象时,返回true。注意,在IE8及以前的版本中,window.alert()和window.attachEvent()等浏览器方法返回false。
jQuery.isPlainObject()
如果参数是“纯”对象,而不是某些特定类型或类对象的实例时,返回true
jQuery.makeArray()
如果参数是类数组对象,该函数会将对象的属性复制到一个新的(真)数组中,并返回该数组。如果参数不是类数组对象,该函数会仅返回一个新数组,该数组只包含传入的参数一个元素。
jQuery.map()/jQuery.merge()/jQuery.parseJSON()/jQuery.proxy()/jQuery.support()/jQuery,trim()
8.jQuery选择器和选取方法
在本章中,我们已经使用了简单CSS选择器的jQuery选取函数:$()。现在是时候深入的了解jQuery选择器语法,以及一些提前和扩充选中元素集的方法了。
i.jQuery选择器
在CSS3选择器标准草案定义的选择器语法中,jQuery支持相当完整的一套子集,同时还添加了一些非标准但很有用的伪类。13.2.iiiii我们描述过基本的css选择器。在此增加更多的高级选择器的阐述。注意:本节讲述的是jQuery选择器。其中有不少选择器(但不是全部)可以在CSS样式表中使用。
选择器语法有三层结构。最简单形式:"#test"选取id为"test"的元素。"blockquote"选取文档中的所有<blockquote>元素,而"div.note"则选取所有class属性为"note"的<div>元素。简单选择器可以组合成"组合选择器",比如"div.note>p"和"blockquote i",只要用组合字符分隔符就行。简单选择器和组合选择器还可以拆分成逗号分隔的列表。这种选择器组是传递给$()函数最常见的形式。在解释组和选择组之前,我们必须莉皎简单选择器语法。
⑴.简单选择器
简单选择器的开头部分(显式或隐身地)是标签类型声明。例如,如果只对<p>元素感兴趣,简单选择器可以用"p"开头。如果选取的元素和标签名无关,则可以使用通配符"*"号来代替。如果选择器没有蚁标签名或通配符开头,则隐式还有一个通配符。
标签名或通配符指定了备选文档元素的一个初始集。在简单选择器中,标签类型声明之后的部分由多个过滤器组成。过滤器从左到右应用,和书写顺序一致,其中每一个都会缩小选中的元素集。
(jQuery选择过滤器请参考:http://file.ahthw.com/jq110/ ,此次略去介绍)
⑵.组合选择器
使用特殊操作符或“组合符”可以将简单选取组合起来,表达文档树中元素的关系。表达文档树中元素之间的关系。下面例举了jQuery支持的组合选择器。这些组合选取与Css3支持的组合选择器是一样的。
jQuery的组合选择器 | |
组合方式 | 含义 |
A B | 从匹配选择器A的元素的子孙元素中,选取匹配选择器B的文档元素。注意在这种中和方式下,组合符就是空白字符 |
A > B | 从匹配选择器A元素的子元素中,选取匹配选择器B的文档元素 |
A + B | 从匹配选择器A的下一个兄弟元素(忽略文本节点和注释)中,选取匹配选择器B的文档元素 |
A ~ B | 从匹配选择器A的元素后面的兄弟元素中,选取匹配选择器B的文档元素 |
下面是组合选择器的一些例子:
"blockquote i" //匹配<blockquote>里的<i>元素 "ol > li" //<li>是<ol>的直接子元素 "#output + *" // id="output"元素后面的兄弟元素 "div.note > h1 + p" //紧跟<h1>的<p>元素,在class = "note"里边
注意组合选择器并不限于组合两个选择器:组合三个甚至更多选择器也是允许的。组合选择器从左往右处理。
⑶.选择器组
传递给$()函数(或在样式表中使用)的选择器就是组合选择器,这是一个逗号分隔的列表,由一个或多个简单选择器或组合选择器构成。选择器组合匹配的元素只要匹配该选择器中的任意一个选取就行。对我们来说,一个简单选择器也可以认为是一个选择器组。下面是选择器组的一些例子:
"h1,h2,h3" //匹配<h1> <h2> <h3>元素 "#p1, #p2, #p3" //匹配id为p1、p2或p3的元素 "div.note, p.note" //匹配class为note的 div和p元素 "body>p,div.note>p" // <body>和class为note的P元素
注意:CSS和jQuery选择器语法允许在简单选择器的某些过滤器中使用圆括号,但不允许使用圆括号来进行更常见的分组。例如,不能把选择器组或组合选择器放在圆括号中并且当成简单选择器:
(h1, h2, h3)+p //非法 h1+p, h2+p , h3+p //正确的写法
ii.选取方法
除了$()函数支持的选择器语法,jQuery还定义了一些选取方法。本章我们已经看到过大部分jQuery都是在选中元素上执行某种操作。选取方法不一样:他们会修改选中元素集,对其进行提取、扩充或仅作为选取操作的起点。
本节描述的这些选取方法。请注意这些选取方法中的多数提供的功能于选取语法的功能是一样的。
提取选中元素最简单的方式是按位置提取。first()返回jQuery对象仅包含选中元素中的第一个,last()返回jQuery对象则只包含一个元素。更通用的是,eq()方法返回的jQuery对象只包含指定序号的单个选中元素。(在jQuery 1.4中,负序号也是允许的,会从选取的末尾开始计数。)注意这些方法返回的jQuery对象只有一个元素。这与常见的数组序号是不一样的,数组序号返回的单一元素没有经过jQuery包装:
var paras = $("p"); paras.first() //选取第一个<p>元素 paras.last() //选取最后一个<p> paras.eq(1) //选取第二个<p> paras.eq(-2) //选取倒数第二个<p> paras[1] //第二个<p>元素自身
通过位置提取选取更通用的方法是slice()。jQuery的slice()方法与Array.slice()方法类似:前者接受开始和结束序号(负号会从结尾处计算),返回的jQuery对象包含从开始到结束序号(但不包含结束序号)处的元素集。如果省略结束序号,返回的对象会包含从开始序号起的所有元素。
$("p").slice(2,5) //选取第3 4 和第5个P元素 $("div").slice(-3) // 选取最后3个<div>元素
filter()是通用的选取过滤方法,有3种调用方式:
$("div").filter(".note") //与$("div.note")一样 $("div").filter($(".note")) //与$("div.note") 一样 $("div").filter(function(idx){ return idx%2==0 }) //与$("div:even") 一样
not()方法与fliter()一样,除了含义与fliter()相反。如果传递选择器字符串给not(),它会返回一个新的jQuery对象,该对象只包含不匹配该选择器的元素。如果传递给jQuery对象、元素数组或单一元素给not(),它会返回包含不匹配该选择器的元素。如果传递jQuery对象、元素数组或单一元素给not(),它会返回除了显式排除的元素之外的所有选中元素。如果传递判断函数给not(),该判断函数的调用就与在filter()中一样,只是返回的jQuery对象仅包含哪些使得判断函数返回false或其他假值的元素:
$("div").not("#header,#footer"); //除了两个元素之外的所有<div>元素
在jQuery 1.4中,提取选取的另一种方式是has()方法。如果传入选择器,has()会返回一个新的jQuery对象,仅包含有子孙元素匹配该选择器的选中元素。如果传入文档元素给has(),它会将选中元素即调整为那些是指定元素祖先节点的选中元素:
$("p").has("a[href]") //包含链接的段落
add()方法会扩充选取,而不是对其进行过滤或提取。可以将传给$()函数的任何参数(除了函数)照样传给add()方法。add()方法会返回原来的选中元素,加上传给$()函数的那些参数所选中(或创建)的那些元素。add()会移除重复元素,并对该组合选取进行排序,以便利民的元素按照文档中的顺序排列:
//选取所有<div>和所有<p>元素的等价方式 $("div,p") //使用选择器组 $("div").add(p) //给add()传入选择器 $("div").add($("p")) //给add()传入jQuery对象 var paras = document.getElementsByTagName("p"); //类数组对象 $("div").add(paras); //给add()传入元素数组
⑴.将选中元素即用做上下文
上面描述的filter()、add()、和not()方法会在各自的选中元素集上执行交集、并集和差集运算。jQuery还定义一些其他选取方法可将当前选中元素集作为上下文来使用。对选中的每一个元素,这些方法会使用该选中元素作为上下文或起点来得到新的选中元素集,然后返回一个新的jQuery对象,包含所有新的选中元素的并集。与add()方法类似,会移除重复元素并进行排序,以便元素按照在文档中出现的顺序排列好。
该类别选取方法中最通用的是find()。它会在每一个当前选中元素的子孙元素中寻找与指定选择器字符串匹配的元素,然后它返回一个新的jQuery对象来代表所匹配的子孙元素集。注意这些新选中的元素不会并入已经村长的选中元素集中。同时注意find()和filter()不同,filter()不会选中新元素,只是简单地将当前选择的元素进行缩减:
$("div").find("p") //在<div>中查找<p>元素,与$("div p")相同
该类别中的其他方法返回新的jQuery对象,代表当前选中元素集中每一个元素的子元素、兄弟元素或父元素。大部分都接受可选的选择器字符串作为参数。不传入选择器时,它会返回所有子元素、兄弟元素或父元素。传入选择器时,他们会过滤元素集。传入选择器时,他们会过滤元素集,仅限返回匹配的。
children()方法返回诶一个选中元素的直接子元素,可以用可选的选择器参数进行过滤:
//寻找id为"header"和"footer"元素自及诶按元素中的<span>元素 // 与$("#header>span, #footer>span")相同 $("#header,#footer").children("span")
contents()方法与children()方法类似,不同的是它会返回每一个元素的所有子节点,包括文本节点。如果选中元素集中有<iframe>元素,contents()还会返回该<iframe>内容的文档内容。注意contens()不接受可选的选择器字符参数————因为它返回的文档节点不完全是元素,而是选择器字符串仅用来描述元素的节点。
next()和prev()方法返回每一个选中元素上一个和上一个兄弟元素(如果有的话),如果传入了选择器,就只会匹配该选择器的兄弟元素:
$("h1").next("P") //与$("h1+p")相同 $("h1").prev() //<h1>元素前面的兄弟元素
nextAll()和prevAll()返回每一个选中元素前面或后面的所有兄弟元素(如果有的话)。siblings()方法则返回每一个选中元素的所有兄弟元素(选中元素本身不是自己的兄弟元素)。如果这些方法传入选择器,则只会返回匹配的兄弟元素:
$("#footer").nextAll("p") //紧跟#footer元素的所有<p>兄弟元素 $("#footer").prevAll() //#footer元素前面的所有兄弟元素
jQuery1.4开始,nextUntil()和prevUntil()方法接受一个选择器参数,会选取选择元素后面或前面的所有兄弟元素,知道找到某个匹配该选择器的兄弟元素为止。如果省略该选择器,这两个方法的作用域就和不带选择器的nextAll()和prevAll()一样。
parent()方法返回每一个选中元素的父节点:
$("li").parent() //列表元素的父节点,比如<ul>和<ol>元素
parents()方法返回每一个选中元素的祖先节点(向上直到<html>元素)。parent()和parents()都接受一个可选的选择器字符串参数:
$("a[href]").parents("p") //含有链接的p元素
parentsUntil()返回每一个选中元素的祖先元素,直到出现匹配指定选择器的第一个祖先元素。closest()方法必须传入一个选择器的字符串,会返回每一个选中元素的祖先元素中匹配该选择器的最近一个祖先元素(如果有的话)。对于方法而言,元素被认为是自身的祖先元素,在jQuery1.4中,还可以给closet()传入一个祖先元素作为第二个参数,用来阻止jQuery往上查找时超越该指定元素:
$("a[href]").closest("div") //包含链接是最里层的<div> $("a[herf]").parentUntil(":not(div)") //所有包裹<a>的<div>元素
⑵.恢复到之前的选中元素集
为了实现方法链式调用,很多jQuery对象的方法最后都返回调用对象。然而本节讲述的方法都返回新的jQuery对象。可以链式调用下去,但必须清晰的意识到,在链式调用的后面所操作的元素集,可能已经不是该链式调用开始时的元素集了。
实际情况还要复杂些。当这里所描述的选取方法在创建或返回一个新的jQuery对象时,它们会给该对象添加一个到它派生自旧的jQuery对象的内部引用。这会创建一个jQuery对象的内部引用。这会创建一个jQuery对象的链式表或栈。end()方法用来弹出栈,返回保存的jQuery对象。在链式调用中调用end()会将匹配元素还原到之前的状态。考虑如下代码:
//寻找所有的<div>元素,然后在其中寻找<p>元素 //高亮显示<p>元素,然后给<div>元素添加一个边框 //首先,不使用链式调用 var divs = $("div"); var paras = div.find("p"); paras.addClass("heiglight"); divs.css("border","solid black 1px"); //下面展现如何使用链式调用来实现 $("div").find("p").addClass("heiglight").end().css("border","solid black 1px"); //还可以将操作调换顺序来避免调用end() $("div").css("border","solid block 1px").find("p").addClass("heiglight");
如果想手动定义选中元素集,同时保持与end()方法的兼容,可以将新的元素集作为数组或类数组对象传递给pushStack()方法。指定的元素会成为新的选中元素,之前选中的元素集则会压入栈中,之后可以用end()方法还原他们:
var sel = $("div"); //选取所有div元素 sel.pushStack(document.getElementsByTagName("p")); //修改为所有p元素 sel.end(); //还原为div元素
既然我们已经讲解了end()方法及其他使用的选区栈,就有最后一个方法需要讲解。andSelf()返回一个新的jQuery对象,包含当前所选中元素,加上之前的所有选中元素(会去除重复的)。andSelf()和add()方法一样,或许"addPrev"是一个更具描述性的名字。作为例子,考虑上面代码的下述变化,或许“addPrev”是一个更具有描述性的名字。作为例子,考虑上面代码的下述变化:高亮显示<p>元素及其父节点的<div>元素,然后给这些div加上边框:
$("div").find("p").andSelf(). addClass("highligeht"). end().end().css("border","solod black 1px");
9.jQuery的插件扩展
jQuery的写法是的添加新功能很是方便。添加新功能的模块称为插件(plug-in),可以在这里找到很多插件:http://plugins.jquery.com。jQuery插件是普通的javascript代码文件,在网页中使用时,只需要用 javascript的src方式引入就好,就和引用其它javascript类库一样,注意:必须在jQuery之后引入jquery插件
开发jQuery插件非常简单。关键点是要知道jQuery.fn是所有jQuery对象的原型对象。如果给该对象添加一个函数,该函数就会成为一个jQuery方法,例子如下:
jQuery.fn.println = function() { //将所有参数合并成空格分隔的字符串 var msg = Array.prototype.join.call(arguments," "); //遍历jQuery对象中的每一个元素 this.each(function(){ //将参数字符串做为春文本添加到每一个元素后面,并添加一个<br/> jquery(this).append(document.createTextNode(msg).append("<br/>")); }); //返回这个未添加的jQuery对象,以便链式调用 return this; }
通过上面对jQuery.fn.parintln()函数的定义,我们可以在任何jQuery对象上类似如下调用println()方法了:
$("#debug").println("x = ", x, "; y = ", y);
这是添加新方法到jQuery.fn中常见的开发方式。如果发现自己在使用each()方法“手动”遍历jQuery对象中的元素,并在元素上执行某些操作时,就可以问问自己,是否可以将代码重构下,使得这些each()回调移动到一个扩展方法里(jQuery插件的这种扩展方式是全局性的,带来方便的同时,也污染了jQuery对象,容易造成潜在冲突,译者并不推荐“随时着想”使用这种扩展方式)。在开发扩展功能时,如果遵守基本的模块化代码实践,以及遵守jQuery特定的一些传统约定,就可以将该扩展成为插件,与他人分享。下面是一些值得留意的jQuery插件约定:
(function($){ //带有参数名为$的参数 // 在这里书写插件的代码 }(jQuery));
//插件可以给jQuery自身增加函数来添加新的工具函数。例如: //插件输出其参数(使用println()方法) //到id为"debug"的元素上。如果不存在该元素,则创建一个并添加到新的文档中 jQuery.debug = function(){ var elt = jQuery("#debug");//查找#debug元素 if(elt.length == 0){ //如果它不存在,则创建之 elt = jQuery("<div id='debug'><h1>debugging output</h1></div>"); jQuery(document.body).appedn(elt); } elt.prinltln.apply(elt,arguments); //将参数输出到元素中 };
除了定义新方法,还可以扩展jQuery类库的其他部分。例如,在5节中,我们已经看到可以通过jQuery.fx.speeds添加属性来扩充新的红花时长名了(除了"fast"和"slow"),也可以通过给jQuery.easing添加属性来添加新的缓动函数。插件甚至可以扩展jQuery的css选择器引擎!可以通过给jQuery.expr[':']对象添加新的伪类过滤器(比如:first和:input)。下面的例子定义了一个新的draggable过滤器,可以用来返回拥有draggable=true属性的元素:
jQuery.expr[':'].draggable = function(e){ return e.draggable === true; };
使用上面定义的选择器,可以用$("img[draggable=true]")来选取可拖拽的图片,而不用使用冗长的$("img[draggable=true]")。
从上面的代码中可以看出,自定义选择器函数的第一个参数是候选的DOM元素。如果改元素匹配选择器,则返回true;否则返回false。许多定义选择器只需这一个元素参数,但实际上在调用时他们传入了4个参数。第二个参数是整数序号,表示当前元素在候选数组中的位置。候选元素数组作为第4个参数传入,选择器不应该修改它。第三个参数是很有趣的:这是调用RegExp.exec()方法返回的数组,如果有的话,该数组的第4个元素(序号是3)是伪类过滤后面的圆括号的中的值。圆括号和里面的人和引号都是去除了,只留下参数字符串。下面是一个例子,用来说明如何实现一个:data(x)伪类,该伪类只在元素拥有data-x属性时返回true.
jQuery.expr(':').data = function (element,index,match,array){ //ie7以下不支持hasAttriBute() return element.hasAttribute("data-" +match[3]); };
10.jQuery UI类库
jQuery限定自己只提供核心的DOM、CSS、事件处理以及Ajax功能。这提供了一个很棒的基础,可以用来构建更高的抽象,比如用户界面组件,jQuery UI类库就是这么做的。
类如其名,jQuery UI定义了一些用户界面组件:输入区域的自动完成,输入日期的日期选择器,用来租住信息的手风琴页和标签页、可视化数字的滑块和进度条,以及用来和用户紧急通信的模态对话框。除了这些组件,jQuery UI还实现了更一般化的“交互”,使得元素轻松就可以实现拖拽,放置,改变大小,可选取,可排序。最后,jQuery UI还给自身的效果方法提供了一些新的视觉效果方法,(还使得可以变化颜色)。同时可以定义很多新的缓动函数。
JQuery UI是完全皮肤化的,它的皮肤可以采用CSS文件形式,因此除了要加载的jQuery UI的js文件到网页中,还需要引入皮肤的CSS文件。jQuery UI预定好了几套皮肤供下载。
jQuery UI组件和交互功能采用jQuery插件方法构建,每一个都定义一个jQuery方法。通常,已存在的文件元素中调用方法是,会将元素转化为组件。例如:要改变输入文本,一遍在单击或聚焦文本输入框时要它弹出一个日期选取组件,直接用下面的代码调用datapicker()就行:
//将class="date"的input元素转化为日期选取组件 $("input.date").datepicker();
灵活的使用jQuery UI组件,需要熟悉三样东西:它的配置选项、它的方法以及它的事件。所有jQuery UI组件都是可配置的,有一些组件有很多配置选项。可以给组件方法传递选项对象(和在动画操作里,传递选项对象给animate()类似)来定义组件的外观和行为。
jQuery UI组件通常会定义至少有几个“方法”来与组件交互,但是,为了避免jQuery方法的迅速增多,jQuery UI组件不会将它们的“方法”定义称为真正的方法。每个组件只会有一个方法(与上面的datapicker()方法一样)。当需要调用组件的一个“方法”,时,需要给组件定义真正的方法传递预期“方法”名称,例如:想要禁止日期选取组件,不能调用disableDatapicker()方法。而需要调用datepicker("disable")。
jQuery UI组件通常会定义自定义事件,想要用户交互时触发它们。可以用常用的bind()方法来给这些自定义事件绑定事件处理程序,通常还可以将事件处理程序函数作为选项对象的属性,该选项对象会传递给组件方法。事件处理程序的第一个参数依旧是Event对象。某些组件还会传递一个“UI”对象作为事件处理程序的第二个参数。该对象通常提供了当前组件的状态信息。
注意:jQuery UI文档中有时描述的“事件”并不是真正的自定义事件,可能描述为回调函数更好。这些回调函数是通过配置选项对象设置的。例如,日期选取组件支持不少回调函数,可以在不同的时间点调用它。但是,这些函数中没有一个拥有标准的事件处理程序签名,不能使用bind()为这些“事件”注册处理程序。
正确的做法是:在初始调用组件方法时,给组件配置选项时,就指定合适的回调函数。
(本章完结)
上一章:第十六章:脚本化HTTP 下一章:第十八章:客户端存储