4.0 第4章 jQuery中的事件和动画
第4章 jQuery中的事件和动画
JavaScript和HTML之间的交互是通过用户和浏览器操作页面时引发的事件来处理的。当文档或者它的某些元素发生某些变化或操作时,浏览器会自动生成一个事件。例如当浏览器装载完一个文档后,会生成事件;当用户单击某个按钮时,也会生成事件。虽然利用传统的JavaScript事件能完成这些交互,但jQuery增加并扩展了基本的事件处理机制。jQuery不仅提供了更加优雅的事件处理语法,而且极大地增强了事件处理能力。
4.1 jQuery中的事件
4.1.1 加载DOM
以浏览器装载文档为例,在页面加载完毕后,浏览器会通过JavaScript为DOM元素添加事件。在常规的JavaScript 代码中,通常使用 window.onload 方法,而在jQuery 中,使用的是$(document).ready()方法。$(document).ready()方法是事件模块中最重要的一个函数,可以极大地提高 Web 应用程序的响应速度。jQuery就是用$(document).ready()方法来代替传统 JavaScript的window.onload 方法的。通过使用该方法,可以在 DOM 载入就绪时就对其进行操纵并调用执行它所绑定的函数。在使用过程中,需要注意$(document).ready()方法和window.onload方法之间的细微区别。
1.执行时机
$(document).ready()方法和window.onload 方法有相似的功能,但是在执行时机方面是有区别的。window.onload 方法是在网页中所有的元素(包括元素的所有关联文件)完全加载到浏览器后才执行,即JavaScript此时才可以访问网页中的任何元素。而通过jQuery中的$(document).ready()方法注册的事件处理程序,在DOM完全就绪时就可以被调用。此时,网页的所有元素对jQuery而言都是可以访问的,但是,这并不意味着这些元素关联的文件都已经下载完毕。
举一个例子,有一个大型的图库网站,为网页中所有图片添加某些行为,例如单击图片后让它隐藏或显示。如果使用window.onload方法来处理,那么用户必须等到每一幅图片都加载完毕后,才可以进行操作。如果使用jQuery中的$(document).ready()方法来进行设置,只要DOM就绪就可以操作了,不需要等待所有图片下载完毕。很显然,把网页解析为DOM树的速度比把页面中的所有关联文件加载完毕的速度快很多。
另外,需要注意一点,由于在$(document).ready()方法内注册的事件,只要DOM 就绪就会被执行,因此可能此时元素的关联文件未下载完。例如与图片有关的HTML下载完毕,并且已经解析为DOM树了,但很有可能图片还未加载完毕,所以例如图片的高度和宽度这样的属性此时不一定有效。要解决这个问题,可以使用jQuery中另一个关于页面加载的方法—load()方法。load()方法会在元素的onload事件中绑定一个处理函数。如果处理函数绑定给window对象,则会在所有内容(包括窗口、框架、对象和图像等)加载完毕后触发,如果处理函数绑定在元素上,则会在元素的内容加载完毕后触发。jQuery代码如下:
[插图]
等价于JavaScript中的以下代码:
[插图]
2.多次使用
第一章曾经用一个表格(表1-2)总结过windows.onload方法和$(document).ready()方法的区别,现在进行详细讲解。
假设网页中有两个函数,JavaScript代码如下:
[插图]
当网页加载完毕后,通过如下JavaScript代码来分别调用one函数和two函数:
[插图]
然而当运行代码后,发现只弹出字符串“two”对话框,如图4-1所示。
[插图]
字符串“one”对话框不能被弹出的原因是 JavaScript的onload 事件一次只能保存对一个函数的引用,它会自动用后面的函数覆盖前面的函数,因此不能在现有的行为上添加新的行为。
为了达到两个函数顺序触发的效果,只能再创建一个新的JavaScript 方法来实现,Javascript代码如下:
[插图]
虽然这样编写代码能解决某些问题,但还是不能满足某些需求,例如有多个JavaScript文件,每个文件都需要用到window.onload方法,这种情况下用上面提到的方法编写代码会非常麻烦。而jQuery的$(document).ready()方法能够很好地处理这些情况,每次调用$(document).ready()方法都会在现有的行为上追加新的行为,这些行为函数会根据注册的顺序依次执行。例如如下jQuery代码:
[插图]
运行代码后,会先弹出字符串“one”对话框,然后弹出字符串“two”对话框,依次显示图4-2和图4-3所示的效果。
[插图]
[插图]
3.简写方式
如果读者注意过本书前几章的例子,会发现例子中并不是用的下面的代码:
[插图]
而是用的下面的代码:
[插图]
后者是前者的简写方式。
另外,$(document)也可以简写为$()。当$()不带参数时,默认参数就是“document”,因此可以简写为:
[插图]
3种方式都是一样的功能,读者可以根据自己的喜好,选择其中的一种。
4.1.2 事件绑定
在文档装载完成后,如果打算为元素绑定事件来完成某些操作,则可以使用bind()方法来对匹配元素进行特定事件的绑定,bind()方法的调用格式为:
[插图]
bind()方法有3个参数,说明如下。
第1个参数是事件类型,类型包括:blur、focus、load、resize、scroll、unload、click、dblclick、mousedown、mouseup、mousemove、mouseover、mouseout、mouseenter、mouseleave、change、select、submit、keydown、keypress、keyup和error等,当然也可以是自定义名称。
第2个参数为可选参数,作为event.data属性值传递给事件对象的额外数据对象。
第3个参数则是用来绑定的处理函数。
1.基本效果
下面通过一个示例来了解bind()方法的用法。
假设网页中有一个FAQ,单击“标题”链接将显示内容。
HTML代码如下:
[插图]
应用CSS样式表后,网页效果图如图4-4所示。
[插图]
按照需求,需要完成以下几个步骤。
(1)等待DOM装载完毕。
(2)找到“标题”所在的元素,绑定click事件。
(3)找到“内容”元素,将“内容”元素显示。
根据分析的步骤,可以轻易地写出如下jQuery代码:
[插图]
运行代码,单击“标题”链接,“内容”就展开了,效果如图4-5所示。
[插图]
在上面的例子中,为“标题”绑定了一个click事件,单击标题链接后,显示“内容”。
与ready()方法一样,bind()方法也可以多次调用。
上面jQuery代码中有一个关键字this,与在JavaScript中的作用一样,this引用的是携带相应行为的DOM元素。为了使该DOM元素能够使用jQuery中的方法,可以使用$(this)将其转换为jQuery对象(具体实现方法参见第1章1.4.2小节jQuery对象和DOM对象的相互转换)。
2.加强效果
在上面的例子中,单击“标题”显示出“内容”;再次单击“标题”,“内容”并没有任何反应。现在需要加强效果:第2次单击“标题”,“内容”隐藏;再次单击“标题”,“内容”又显示,两个动作循环出现。为了实现这个功能,需要经过以下几个步骤。
(1)等待DOM装载完毕。
(2)找到“标题”所在的元素,绑定click事件。
(3)找到“内容”元素,如果“内容”元素是显示的,则隐藏,如果“内容”元素是隐藏的,则显示。
加强效果的第
(3)步需要做判断,原理如下:
[插图]
为了判断元素是否显示,可以使用jQuery中的is()方法来完成。jQuery代码如下:
[插图]
在代码中,发现$ (this).next () 被多次使用,因此可以为它定义一个局部变量:
[插图]
然后把局部变量引入到代码中,改进后的jQuery代码如下:
[插图]
通过以上的修改,可以实现加强效果。当反复地单击“标题”链接时,“内容”会在隐藏和显示两种状态下切换。
3.改变绑定事件的类型
上面的例子中,给元素绑定的事件类型是 click,当用户单击的时候会触发绑定的事件,然后执行事件的函数代码。现在把事件类型换成mouseover和mouseout,即当光标滑过的时候,就触发事件。需要进行以下几步操作。
(1)等待DOM装载完毕。
(2)找到“标题”所在的元素,绑定mouseover事件。
(3)找到“内容”元素,显示“内容”。
(4)找到“标题”所在的元素,绑定mouseout事件。
(5)找到“内容”元素,隐藏“内容”。
根据分析的步骤,可以写出如下jQuery代码:
[插图]
代码运行后,当光标滑过“标题”链接后,相应的“内容”将被显示,如图4-6所示。当光标滑出“标题”链接后,相应的“内容”则被隐藏,如图4-7所示。
[插图]
[插图]
在上面几个例子中,分别用bind()方法给“标题”绑定了click事件、mouseover事件和mouseout事件,绑定方法都一样。除此之外,bind()方法还能绑定其他所有的JavaScript事件。
4.简写绑定事件
像click、mouseover和mouseout这类事件,在程序中经常会使用到,jQuery为此也提供了一套简写的方法。简写方法和bind()方法的使用类似,实现的效果也相同,惟一的区别是能够减少代码量。
例如把上面的例子改写成使用简写绑定事件的方式,代码如下:
[插图]
4.1.3 合成事件
jQuery有两个合成事件—hover()方法和toggle()方法,类似前面讲过的ready()方法,hover()方法和toggle()方法都属于jQuery自定义的方法。
1.hover()方法
hover()方法的语法结构为:
[插图]
hover()方法用于模拟光标悬停事件。当光标移动到元素上时,会触发指定的第1个函数(enter);当光标移出这个元素时,会触发指定的第2个函数(leave)。
将上面的例子改写成使用hover()方法,jQuery代码如下:
[插图]
代码运行后的效果与下面代码运行后的效果是一样的。当光标滑过“标题”链接时,相应的“内容”将被显示;当光标滑出“标题”链接后,相应的“内容”则被隐藏。
[插图]
2.toggle()方法
toggle()方法的语法结构为:
[插图]
toggle()方法用于模拟鼠标连续单击事件。第1次单击元素,触发指定的第1个函数(fn1);当再次单击同一元素时,则触发指定的第2个函数(fn2);如果有更多函数,则依次触发,直到最后一个。随后的每次单击都重复对这几个函数的轮番调用。
在前面的加强效果的例子中,使用了以下jQuery代码:
[插图]
虽然上面的代码能实现需要的效果,但是选择的方法并不是最适合的。如果需要连续单击“标题”链接,来达到使“内容”隐藏和显示的目的,那么很适合使用toggle()方法。原理如下:
[插图]
使用toggle()方法来改写上面的例子,jQuery代码如下:
[插图]
通过使用toggle()方法不仅实现了同样的效果,同时也简化了代码。
toggle()方法在jQuery中还有另外一个作用:切换元素的可见状态。如果元素是可见的,单击切换后则为隐藏;如果元素是隐藏的,单击切换后则为可见的。因此上面的代码还可以写成如下jQuery代码:
[插图]
3.再次加强效果
为了能有更好的用户体验,现在需要在用户单击“标题”链接后,不仅显示“内容”,而且高亮显示“标题”。为了完成这一功能,首先在CSS中定义一个高亮的样式,CSS代码如下:
[插图]
接下来需要完成以下几个步骤。
(1)等待DOM装载完毕。
(2)找到“标题”元素,添加toggle()方法,在toggle()方法里定义两个函数,分别代表显示和隐藏。
(3)在显示函数里,给“标题”添加高亮class。
(4)在隐藏函数里,移除“标题”的高亮class。
然后编写如下jQuery代码:
[插图]
运行代码后,如果“内容”是显示的,“标题”则会高亮显示;如果“内容”是隐藏的,则不会高亮显示“新闻标题”,显示如图4-8和图4-9所示效果。
[插图]
[插图]
4.1.4 事件冒泡
1.什么是冒泡
在页面上可以有多个事件,也可以多个元素响应同一个事件。假设网页上有两个元素,其中一个元素嵌套在另一个元素里,并且都被绑定了click事件,同时<body>
元素上也绑定了click事件。完整代码如下:
[插图]
页面初始化效果如图4-10所示。
[插图]
当单击内部<span>
元素,即触发<span>
元素的click事件时,会输出3条记录,如图4-11所示。这就是由事件冒泡引起的。
[插图]
在单击<span>
元素的同时,也单击了包含<span>
元素的元素<div>
和包含<div>
元素的元素<body>
,并且每一个元素都会按照特定的顺序响应click事件。
元素的click事件会按照以下顺序“冒泡”。
(1)<span>
。
(2)<div>
。
(3)<body>
。
之所以称为冒泡,是因为事件会按照 DOM的层次结构像水泡一样不断向上直至顶端,如图4-12所示。
[插图]
2.事件冒泡引发的问题
事件冒泡可能会引起预料之外的效果。上例中,本来只想触发<span>
元素的click事件,然而<div>
元素和<body>
元素的click 事件也同时被触发了。因此,有必要对事件的作用范围进行限制。当单击<span>
元素时,只触发<span>
元素的click事件,而不触发<div>
元素和<body>
元素的click事件;当单击<div>
元素时,只触发<div>
元素的click事件,而不触发<body>
元素的click事件。为了解决这些问题,介绍以下内容。
- 事件对象
由于IE-DOM和标准DOM实现事件对象的方法各不相同,导致在不同浏览器中获取事件对象变得比较困难。针对这个问题,jQuery进行了必要的扩展和封装,从而使得在任何浏览器中都能很轻松地获取事件对象以及事件对象的一些属性。
在程序中使用事件对象非常简单,只需要为函数添加一个参数,jQuery代码如下:
[插图]
这样,当单击“element”元素时,事件对象就被创建了。这个事件对象只有事件处理函数才能访问到。事件处理函数执行完毕后,事件对象就被销毁。
- 停止事件冒泡
停止事件冒泡可以阻止事件中其他对象的事件处理函数被执行。在 jQuery 中提供了stopPropagation()方法来停止事件冒泡。
jQuery代码如下:
[插图]
当单击<span>
元素时,只会触发<span>
元素上的click 事件,而不会触发<div>
元素和<body>
元素的click事件。
可以用同样的方法解决<div>
元素上的冒泡问题。
jQuery代码如下:
[插图]
这样,当单击<span>
元素或者<div>
元素时,就只会输出相应的内容,而不会输出其他的内容,效果如图4-13所示。
[插图]
- 阻止默认行为
网页中的元素有自己默认的行为,例如,单击超链接后会跳转、单击“提交”按钮后表单会提交,有时需要阻止元素的默认行为。
在jQuery中,提供了preventDefault()方法来阻止元素的默认行为。
举一个例子,在项目中,经常需要验证表单,在单击“提交”按钮时,验证表单内容,例如某元素是否是必填字段,某元素长度是否够 6 位等,当表单不符合提交条件时,要阻止表单的提交(默认行为)。
代码如下:
[插图]
当用户名为空时,单击“提交”按钮,会出现图4-14所示的提示,并且表单不能提交。只有在用户名里输入内容后,才能提交表单。可见,prevent Default()方法能阻止表单的提交行为。
[插图]
如果想同时对事件对象停止冒泡和默认行为,可以在事件处理函数中返回false。这是对在事件对象上同时调用stopPrapagation()方法和preventDefault()方法的一种简写方式。
在表单的例子中,可以把
[插图]
改写为:
[插图]
也可以把事件冒泡例子中的
[插图]
改写为:
[插图]
- 事件捕获
事件捕获和事件冒泡是刚好相反的两个过程,事件捕获是从最顶端往下开始触发。
还是冒泡事件的例子,其中元素的click事件会按照以下顺序捕获。
(1)<body>
。
(2)<div>
。
(3)<span>
。
很显然,事件捕获是从最外层元素开始,然后再到最里层元素。因此绑定的click事件,首先会传递给<body>
元素,然后传递给<div>
元素,最后才传递给<span>
元素。
[插图]
遗憾的是,并非所有主流浏览器都支持事件捕获,并且这个缺陷无法通过JavaScript 来修复。jQuery 不支持事件捕获,如果读者需要使用事件捕获,请直接使用原生的JavaScript。
4.1.5 事件对象的属性
jQuery在遵循W3C规范的情况下,对事件对象的常用属性进行了封装,使得事件处理在各大浏览器下都可以正常运行而不需要进行浏览器类型判断。
(1)event.type
该方法的作用是可以获取到事件的类型。
[插图]
以上代码运行后会输出:
[插图]
(2)event.preventDefault()方法
在本章第4.1.4 小节事件冒泡中已经介绍过该方法,该方法的作用是阻止默认的事件行为。JavaScript中符合W3C规范的preventDefault()方法在IE浏览器中却无效。jQuery对其进行了封装,使之能兼容各种浏览器。
(3)event.stopPropagation()方法
在本章第4.1.4小节事件冒泡中已经介绍过该方法,该方法的作用是阻止事件的冒泡。JavaScript中符合W3C规范的stopPropagation()方法在IE浏览器中却无效。jQuery对其进行了封装,使之能兼容各种浏览器。
(4)event.target
event.target的作用是获取到触发事件的元素。jQuery 对其封装后,避免了各个浏览器不同标准的差异。
[插图]
以上代码运行后会输出:
[插图]
(5)event.relatedTarget
在标准DOM中,mouseover和mouseout所发生的元素可以通过event.target来访问,相关元素是通过 event.relatedTarget 来访问的。event.relatedTarget 在mouseover 中相当于 IE 浏览器的event.fromElement,在mouseout中相当于IE浏览器的event.toElement,jQuery对其进行了封装,使之能兼容各种浏览器。
(6)event.pageX和event.pageY
该方法的作用是获取到光标相对于页面的x坐标和y坐标。如果没有使用jQuery时,那么IE浏览器中是用event.x / event.y,而在Firefox浏览器中是用event.pageX /event.pageY。如果页面上有滚动条,则还要加上滚动条的宽度或高度。
[插图]
(7)event.which
该方法的作用是在鼠标单击事件中获取到鼠标的左、中、右键;在键盘事件中获取键盘的按键。比如,获取鼠标的左、中、右键:
[插图]
以上代码加载到页面后,用鼠标单击页面时,单击左、中、右键分别返回1、2、3。
比如,获取键盘的按键:
[插图]
(8)event.metaKey
针对不同浏览器对键盘中的<ctrl>
按键解释不同,jQuery也进行了封装,并规定event.metaKey为键盘事件中获取<ctrl>
按键。
4.1.6 移除事件
在绑定事件的过程中,不仅可以为同一个元素绑定多个事件,也可以为多个元素绑定同一个事件。假设网页上有一个<button>
元素,使用以下代码为该元素绑定多个相同的事件。
[插图]
当单击按钮后,会出现图4-16所示的效果。
[插图]
1.移除按钮元素上以前注册的事件
首先在网页上添加一个移除事件的按钮。
[插图]
然后为按钮绑定一个事件,jQuery代码如下:
[插图]
最后需要为该事件编写处理函数用于删除元素的所有click事件,jQuery代码如下:
[插图]
因为元素绑定的都是click事件,所以不写参数也可以达到同样的目的,jQuery代码如下:
[插图]
下面来看看unbind()方法的语法结构:
[插图]
第1个参数是事件类型,第2个参数是将要移除的函数,具体说明如下。
(1)如果没有参数,则删除所有绑定的事件。
(2)如果提供了事件类型作为参数,则只删除该类型的绑定事件。
(3)如果把在绑定时传递的处理函数作为第2个参数,则只有这个特定的事件处理函数会被删除。
2.移除
首先需要为这些匿名处理函数指定一个变量。
例如下面的jQuery代码:
[插图]
然后就可以单独删除某一个事件了,jQuery代码如下:
[插图]
当单击“删除第二个事件”按钮后,再次单击“点击我”按钮,显示图4-17所示的效果。
[插图]
另外,对于只需要触发一次,随后就要立即解除绑定的情况, jQuery 提供了一种简写方法——one()方法。One()方法可以为元素绑定处理函数。当处理函数触发一次后,立即被删除。即在每个对象上,事件处理函数只会被执行一次。
One()方法的结构与bind()方法类似,使用方法也与bind()方法相同,其语法结构如下:
[插图]
示例代码如下:
[插图]
使用one()方法为<button>
元素绑定单击事件后,只在用户第1次单击按钮时,处理函数才执行,之后的单击毫无作用。
4.1.7 模拟操作
1.常用模拟
以上的例子都是用户必须通过单击按钮,才能触发click事件,但是有时,需要通过模拟用户操作,来达到单击的效果。例如在用户进入页面后,就触发click事件,而不需要用户去主动单击。
在jQuery中,可以使用trigger()方法完成模拟操作。例如可以使用下面的代码来触发id为btn的按钮的click事件。
[插图]
这样,当页面装载完毕后,就会立刻输出想要的效果,如图4-18所示。
[插图]
也可以直接用简化写法click(),来达到同样的效果:
[插图]
2.触发自定义事件
trigger()方法不仅能触发浏览器支持的具有相同名称的事件,也可以触发自定义名称的事件。
例如为元素绑定一个“myClick”的事件,jQuery代码如下:
[插图]
想要触发这个事件,可以使用以下代码来实现:
[插图]
实现效果如图4-19所示。
[插图]
3.传递数据
trigger(type,[data])
方法有两个参数,第1个参数是要触发的事件类型,第2个参数是要传递给事件处理函数的附加数据,以数组形式传递。通常可以通过传递一个参数给回调函数来区别这次事件是代码触发的还是用户触发的。
下面是一个传递数据的例子。
[插图]
[插图]
4.执行默认操作
trigger()方法触发事件后,会执行浏览器默认操作。例如:
[插图]
以上代码不仅会触发为<input>
元素绑定的focus事件,也会使<input>
元素本身得到焦点(这是浏览器的默认操作)。
如果只想触发绑定的focus事件,而不想执行浏览器默认操作,可以使用jQuery中另一个类似的方法——triggerHandler()方法。
[插图]
该方法会触发<input>
元素上绑定的特定事件,同时取消浏览器对此事件的默认操作,即文本框只触发绑定的focus事件,不会得到焦点。
4.1.8 其他用法
前面已经对bind()方法进行了介绍,bind()方法不仅能为元素绑定浏览器支持的具有相同名称的事件,也可以绑定自定义事件。不仅如此,bind()方法还能做很多的事情。
1.绑定多个事件类型
例如可以为元素一次性绑定多个事件类型。jQuery代码如下:
[插图]
当光标滑入<div>
元素时,该元素的class切换为“over”;当光标滑出<div>
元素时,class切换为先前的值。这段代码等同于下面的代码:
[插图]
很显然,第1种方式能减少代码量,这就是jQuery提倡的“write less,do more”(写得更少,做得更多)理念。
2.添加事件命名空间,便于管理
例如可以把为元素绑定的多个事件类型用命名空间规范起来,jQuery代码如下:
[插图]
在所绑定的事件类型后面添加命名空间,这样在删除事件时只需要指定命名空间即可。单击<button>
元素后,“plugin”的命名空间被删除,而不在“plugin”的命名空间的“dblclick”事件依然存在。
删除多个事件代码也可以写为以下链式代码,但显然上面的方式写得更少。
[插图]
3.相同事件名称,不同命名空间执行方法
例如可以为元素绑定相同的事件类型,然后以命名空间的不同按需调用,jQuery代码如下:
[插图]
当单击<div>
元素后,会同时触发click事件和click.plugin事件。如果只是单击<button>
元素,则只触发 click 事件,而不触发 click.plugin 事件。注意,trigger(“click!”)后面的感叹号的作用是匹配所有不包含在命名空间中的click方法。
如果需要两者都被触发,改为如下代码即可:
[插图]
到此,jQuery中的事件已经介绍完了。下面将介绍jQuery中的动画。