您的位置:首页 > Web前端 > JQuery

《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()这类基本的简化方法,学习了这一章之后才了解到还有事件传播、事件委托等重要知识。而且有关事件委托的内容我也是第二遍看书是才完全理解。书本上有些语句叙述地有点难以理解,因此将书本上重要的部分以及自己的理解记录下来,以备日后忘记时回顾一二。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jquery