《jQuery基础教程》学习笔记(一)——jQuery事件
2015-12-04 17:10
603 查看
《jQuery基础教程》学习笔记(一)——jQuery事件
一、事件处理程序综述
总的来说,jQuery用于响应网页的加载事件都是通过事件处理程序$(document).ready()来处理函数中的代码。因为jQuery是一个基于JavaScript的类库,我们当然可以通过js原生的
window.onload来实现相同的效果。但是这二者之间在触发的时间上存在细微的差别。具体如下:
window.onload事件只有在文档全部加载完毕后才会触发,而
$(document).ready()事件处理程序在DOM加载完毕后就可以被调用
这样的差异在规模较小的网页中基本感觉不出来有什么不同。但假如一个网页包含上千张图片,如果采用原生的
window.onload方式则要在所有的图片都加载完毕后才会执行,与之不同,
$(document).ready()在网页DOM加载完毕后就可以执行。
采用第一种方式,在网页尚未加载完毕的情况下,如果用户已经点击了一些具备默认行为的元素(e.g. 链接),则浏览器会跳转到该链接的目标地址,而不是js捆绑的自定义事件。
window.onload在js中只能被绑定一次,而
$(document).ready()可以绑定多个事件。
如果在一个js文件中同时出现了
window.onload = firstEvent; function firstEvent() { //first event... } window.onload = secondEvent; function secondEvent() { //second event... }
则第二次绑定的事件会覆盖第一个绑定的事件,即待网页全部加载完毕后,只会执行函数
secondEvent()中定义的行为。而使用
$(document).ready()绑定多个事件后,每次调用这个方法都会向内部行为队列添加一个新的函数,当页面DOM加载完毕后,这些函数会按照之前注册的顺序依次执行。
当给
$(document).ready()传递一个函数作为参数时,jQuery会隐式地调用
.ready()方法。即,对于:
$(document).ready(function() { //some codes... });
可以简单写成:
$(function() { //some codes... });
不过我个人喜欢第一种方式,显式地声明 .ready() 方式有助于提醒我jQuery事件执行的时间。
二、事件传播
理解jQuery的事件传播机制能够避免一些意想不到的情况,对此加以利用还可以很好的使用事件委托功能。假设一个HTML页面如下:<div class="foo"> <span class="bar"> <a href="http://www.example.com/"> The quick brown fox jumps over the lazy dog. </a> </span> <p> How razorback-jumping frogs can level six piqued gymnasts! </p> </div>
这段代码在浏览器中结构如下:
[b]图1 网页结构[/b]
如果点击了
<a>元素,那么处于同一位置的它的祖先
<span>元素和
<div>元素都可以响应到点击操作。允许多个网页元素响应同一事件的第一种策略是事件捕获。在事件捕获的过程中,事件首先会交给最外层的元素,接着再交给更具体的元素。在这个例子中,意味着单击事件首先会传递给
<div>,然后是
<span>,最后是
<a>。如下图所示。
[b]图2 事件捕获模型[/b]
另一种相反的策略叫做事件冒泡。即当事件发生时,会首先发送给最具体的元素,在这个元
素获得响应机会之后,事件会向上冒泡到更一般的元素。在这个例子中,
<a>会首先处理事件,然后按照顺序依次是
<span>和
<div>。在DOM标准中同时采取这两种策略:首先,事件要从一般元素到具体元素逐层捕获,然后,事件再通过**冒泡返回**DOM树的顶层。而事件处理程序可以注册到这个过程中的任何一个阶段。
因此, 为了确保跨浏览器的一致性, jQuery始终会在模型的冒泡阶段注册事件处理程序。因此,可以认为在jQuery中最具体的元素会首先获得响应事件的机会。
2.1 事件冒泡的副作用
参考这样一段HTML代码:<div id="switcher" class="switcher"> <h3>Style Switcher</h3> <button id="switcher-default">Default</button> <button id="switcher-narrow">Narrow Column</button> <button id="switcher-large">Large Print</button> </div>
相应的jQuery代码如下:
$(document).ready(function() { $('#switcher').click(function() { $('#switcher button').toggleClass('hidden'); }); });
可以看出,这段代码的本意是当点击
<div>时,内部的三个
<button>在隐藏/出现这两个效果中交替;点击三个
<button>按键时,改变网页的字体大小。但是由于事件冒泡的机制,点击
<button>这个事件会冒泡上浮至其上层DOM元素——
<div id="switcher">捕获,从而触发代码
$('#switcher').click(function() {});中定义的内容——使得三个按键隐藏。
2.2 通过事件对象改变事件的旅程
对于上面描述的关于事件冒泡产生负作用,可以有多种方式解决。但是不论采取何种方式,我们必定要通过对事件这一对象进行处理,才能达到我们理想的效果。因此,在jQuery中提出了事件对象 这一术语。事件对象是一种DOM结构,它会在元素获得处理事件的机会时传递给被调用的事件处理程序。这个对象中包含着与事件有关的信息(例如事件发生时的鼠标指针位置),也提供了可以用来影响事件在DOM中传递进程的一些方法。因此,我们需要给相应的函数添加一个参数作为处理程序中的事件对象。习惯上我们将事件对象命名为event。$(document).ready(function() { $('#switcher').click(function(event) { $('#switcher button').toggleClass('hidden'); }); });
2.2.1 事件目标
由上面描述可知,事件处理程序中的变量event保存着事件对象。因此,只需要对变量event进行相应操作就可以改变事件。在DOM API中规定了event有一个target属性,保存着发生事件的目标元素。通过.target,可以确定DOM中首先接收到事件的元素(即实际被单击的元素)。因此,2.1中提到的事件传播的副作用可以通过判断事件目标(.target)来避免。
$(document).ready(function() { $('#switcher').click(function(event) { if (event.target == this) { $('#switcher button').toggleClass('hidden'); } }); });
这里的event代表当文件加载完毕后的点击事件;.target代表了具体是哪一个 DOM 元素触发了点击事件;this引用的是捕捉到点击操作的DOM元素
<div id="switcher">。当点击
<div id="switcher">的内部DOM元素(如
<button>或者
<h3>)时,event.target代表被点击的是
<button>元素或
<h3>元素,若此时
event.target == this,即被
<div id="switcher">捕获到的click事件是通过点击自身而不是通过点击子元素
<button>或
<h3>冒泡而来,在满足这种条件下,相应元素才进行隐藏/显示效果。因此,上述代码通过判断事件目标来源的方式阻止了事件冒泡。
2.2.2 停止事件传播
除了通过根据判断事件目标的方式来阻止事件传播的方式外,jQuery还提供了一个一劳永逸的方式——通过事件对象的.stopPropagation()方法来完全阻止事件冒泡。所以,我们可以对2.2.1中的代码进行修改,去掉if条件判断,而是在<button>的click事件中调用.stopPropagation()方法阻止了事件的传播。
$(document).ready(function() { $('#switcher').click(function(event) { $('#switcher button').toggleClass('hidden'); }); });
$(document).ready(function() {
$('#switcher button').click(function(event) {
//some code...
event.stopPropagation();
});
});
正常情况下,点击了
<button id="switcher-*">之后,会将这个事件冒泡至其所有的DOM祖先节点,但是由于
event.stopPropagation()方法的调用,阻止了click事件的传播,从而避免其祖先节点响应这个事件。
三 事件委托
3.1 有效地利用事件传播机制
我们回到2.2.1中的例子,通过if语句可以排除点击<div id="switcher">内部节点的情况,不过,如果我们添加了else语句,就可以处理点击
<div id="switcher">内部节点的时间;更进一步,通过判断
event.target的值还可以细分到底是点击
<div id="switcher">事件、点击
<button>事件还是点击
<h3>事件。所有的这些操作都只需要在
$('#switcher').click(function(event) {}中完成即可,相比于在
<div>、
<button>和
<h3>中分别定义函数操作以及相应地调用
event.stopPropagation()方法,不仅节省了代码编写,而且jQuery只需要遍历一次DOM元素就可以完成三种情况的操作,这在大规模网页代码效率的影响是很重要的。例如,有一个显示信息的大型表格,每一行都有一项需要注册单击处理程序。虽然不难通过隐式迭代来指定所有单击处理程序,但性能可能会很成问题,因为循环是由jQuery在内部完成的,而且要维护所有处理程序也需要占用很多内存。
为解决这个问题,可以只在DOM中的一个祖先元素上指定一个单击处理程序。由于事件会冒泡,未遭拦截的单击事件最终会到达这个祖先元素,而我们可以在此时再作出相应处理。因此,我们可以看到,事件冒泡并不总是带来问题,也可以利用它为我们带来好处。 事件委托就是利用冒泡的一项高级技术。通过事件委托,可以借助一个元素上的事件处理程序完成很多工作。
$(document).ready(function() { $('#switcher').click(function(event) { if ($(event.target).is('button')) { //some code... } else { $('#switcher button').toggleClass('hidden'); } }); });
<div id="switcher>是转换器的祖先元素,因此在这个div中不论点击什么元素,最终都会由于事件冒泡作用而被
<div id="switcher>捕获。之后再通过事件target属性来分配具体操作,减少了多次遍历DOM元素的工作,提高了效率。
3.2 内置的事件委托功能
由于事件委托可以解决很多问题,jQuery提供了.on()方法可以接受相应参数实现事件委托。$('#switcher').on('click', 'button', function() { //some code... });
这种做法是将
<div id="switcher">内部所有DOM元素(包括
<button>和
<h3>)的click事件委托给
<div id="switcher">,从而当每次
<div id="switcher">检测到点击事件时(不论是电机自身瀚还是子元素事件冒泡而来),jQuery会把click事件处理程序绑定到#switcher对象,同时比较
event.target和选择符表达式(
.on()方法的第二个参数 ,这里是’button’)是否匹配,如果是,jQuery会把this关键字映射到匹配的元素(一切关于button的操作不再需要
$('#button')或
event.target来查找,只需通过
$(this)即可),完成该方法定义的事件。
四 改变事件绑定
4.1 解除绑定
有时候,我们需要停用以前注册的事件处理程序。可能是因为页面的状态发生了变化,导致相应的操作不再有必要。处理这种情形的一种典型做法,就是在事件处理程序中使用条件语句。但是,如果能够完全移除处理程序绑定显然更有效率。还是回到一开始引出的那个例子。
<div id="switcher">是一个样式选择器,里面包含三个按钮。
<div id="switcher" class="switcher"> <h3>Style Switcher</h3> <button id="switcher-default">Default</button> <button id="switcher-narrow">Narrow Column</button> <button id="switcher-large">Large Print</button> </div>
当点击Narrow Column按钮时页面为小号字体、宽度为屏幕一般;当点击Large Print按钮时页面大字体显示;当点击Default按钮时页面以默认格式显示。此外,当点击
<div id="switcher">内部而不是
<button>时,三个按钮隐藏。
上述要求通过前面的事件委托和阻止事件传播均可实现,下面来考虑一些额外要求。假设我们希望折叠样式转换器
<div id="switcher">在页面没有使用正常样式的情况下保持扩展状态,即当Narrow Column或 Large Print按钮被选中时,单击样式转换器的背景区域不应该引发任何操作。为此,可以在单击非默认样式转换按钮时,调用.off()方法移除折叠处理程序。
$(document).ready(function() { $('#switcher').click(function(event) { if (!$(event.target).is('button')) { $('#switcher button').toggleClass('hidden'); } }); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').off('click'); }); });
现在,如果单击Narrow Column按钮,样式转换器(
<div>)上的单击处理程序就会被移除。然后,再单击背景区域将不会导致它折叠起来。但是,按钮本身的作用却失效了!这是因为我们将点击
<button>事件委托给
<div>来处理,由于之前点击
<button>使得
<div>通过.off()方法移除了click事件,因此,委托给
<div>的click
<button>事件也由于
<div>click事件的移除而消失。换句话说,在调用
$('#switcher').off('click')时,会导致按钮上绑定的两个事件处理程序都被移除。
4.2 为事件处理程序添加命名空间
因此,应该让对.off()的调用更有针对性,以避免把注册的两个单击处理程序全都移除。一种实现方式是使用事件命名空间,即在绑定事件时引入附加信息,以便将来识别特定的处理程序。要使用命名空间,必须使用绑定事件处理程序的非简写方法,即.on()方法本身。我们为.on()方法传递的第一个参数,应该是想要截获的事件的名称。不过,在此可以使用
一种特殊的语法形式,即对事件加以细分。
$(document).ready(function() { $('#switcher').on('click.collapse', function(event) { if (!$(event.target).is('button')) { $('#switcher button').toggleClass('hidden'); } }); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').off('click.collapse'); }); });
对于事件处理系统而言,后缀.collapse是不可见的。这里仍然会像编写
.on('click')一样,让注册的函数响应单击事件。但是,通过附加的命名空间信息,则可以解除对这个特定处理程序的绑定,同时不影响为按钮注册的其他单击处理程序。换句话说,对于
<div>捕获的所有click事件,我们将其分为两类,其中一类放置在一个称为.collapse的明命名空间,它特指隐藏
<button>的click事件。因此,我们将这一特定事件解绑并不会影响到另一个click事件——即改变页面显示的click事件。
4.3 重新绑定事件
现在单击Narrow Column或Large Print按钮,会导致样式转换器的折叠功能失效。可是,我们希望该功能在单击Default按钮时恢复。为此,应该在Default按钮被单击时, 重新绑定事件处理程序。$(document).ready(function() { var toggleSwitcher = function(event) { if (!$(event.target).is('button')) { $('#switcher button').toggleClass('hidden'); } }; $('#switcher').on('click.collapse', toggleSwitcher); });
这里使用了另一种定义函数的语法,即没有使用函数声明(前置function关
键字) ,而是将一个匿名函数表达式指定给了一个局部变量,也叫做命令函数。它有以下两点好处:
将函数表达式指定给局部变量,在后续调用该函数时可以直接用该变量来代替,避免了大段的函数代码块,易于阅读;
使用命令函数还可以省去命名空间的麻烦,因为.off()操作可以讲这个可以将这个命名函数作为第二个参数。换句话说,
$('#switcher').off('click', toggleSwitcher)相当于
$('#switcher').on('click.toggleSwitcher')。
不过,上述代码还存在另一个问题,在jQuery中把处理程序绑定到事件时,之前绑定的处理程序仍然有效。在这个例子中,每次点击Normal,就会有一个toggleSwitcher的副本被绑定到样式转换器。在用户单击Narrow或Large Print之前(这样就可以一次性地解除对toggleSwitcher的绑定),每多单击一次都会多调用一次这个函数。
在绑定toggleSwitcher偶数次的情况下,单击样式转换器(不是按钮),好像一切都没有发生变化。事实上,这是因为切换了hidden类偶数次,结果状态与开始的时候相同。为了解决这个问题,可以在用户单击任意按钮时解除绑定,并在确定单击按钮的ID是switcher-default的情况下再重新绑定
$(document).ready(function() { var toggleSwitcher = function(event) { if (!$(event.target).is('button')) { $('#switcher button').toggleClass('hidden'); } }; $('#switcher').on('click', toggleSwitcher); $('#switcher button').click(function() { $('#switcher').off('click', toggleSwitcher); if (this.id== 'switcher-default') { $('#switcher').on('click', toggleSwitcher); } }); });
五 键盘事件
对于敲击键盘事件,主要分为两类:对键盘按键给出响应的事件(keyup和keydown)
对文本输入给出响应的事件(keypress)
keyup和keydown只关心按下的是哪个键,keydown 事件会在键盘按下时触发,而
keyup 事件会在按键释放时触发。keypress则关心用户输入的是什么字符,例如只按A键输入的文本应该是小写字母a,而同时按Shift和A键,输入的文本是大写字母A。
在这个例子中,我们想实现按下D键相当于点击Default按钮功能,按下N键相当于点击Narrow按钮功能,按下L键相当于点击Large按钮功能。所以,我们并不关心用户输入的具体是大写字母还是小写字母,因此,我们只需要调用keyup()方法即可。
敲击键盘后,需要知道按下的具体某个键的值,jQuery提供了了两种方法:
event.keyCode()
String.fromCharCode(event.which)
event.keyCode()获得相应的ASCII码值,例如按下上、下、左、右键,分别返回38、40、37和39;对于String.fromCharCode(event.which)而言,事件对象的.which属性包含着被按下的那个键的标识符。对于字母键而言,这个标识符就是相应大写字母的ASCII值。
$(document).ready(function() { var triggers = { D: 'default', N: 'narrow', L: 'large' }; $(document).keyup(function(event) { var key = String.fromCharCode(event.which); if (key in triggers) { $('#switcher-' + triggers[key]).click(); } }); });
至此,《jQuery基础教程》第三章《事件》就学习结束了。当然,jQuery事件的内容不仅仅只有这些,还有许多更深入的知识有待学习。一开始,我仅仅会使用.click()这类基本的简化方法,学习了这一章之后才了解到还有事件传播、事件委托等重要知识。而且有关事件委托的内容我也是第二遍看书是才完全理解。书本上有些语句叙述地有点难以理解,因此将书本上重要的部分以及自己的理解记录下来,以备日后忘记时回顾一二。
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- JavaScript演示排序算法
- 2015-2016网页设计趋势分析 Web Design of Trends
- jQuery Ajax 跨域调用
- 移动端的长按事件
- jquery教程靠边站,一分钱不花让你免费学会jquery
- JQuery+Strusts1.x无刷新登录
- JavaScript 各种遍历方式详解
- jQuery菜单插件用法实例
- JQuery 初体验(建议学习jquery)
- 基于Jquery和CSS3制作数字时钟附源码下载(CSS3篇)
- Jquery实现的table最后一行添加样式的代码
- jQuery实现向下滑出的平滑下拉菜单效果
- jQuery 练习[一] 学习jquery的准备工作
- jquery获得页面元素的坐标值实现思路及代码
- 使用Browserify配合jQuery进行编程的超级指南
- jquery如何实现在加载完iframe的内容后再进行操作
- jquery $.ajax()取xml数据的小问题解决方法
- jQuery '行 4954 错误: 不支持该属性或方法' 的问题解决方法
- Jquery 表单取值赋值的一些基本操作