事件在Web应用程序中是有魔力的要素。如果希望Web应用程序应该流畅并且不唐突地响应用户操作,就像是桌面应用程序一样。能够让Web应用程序更具有桌面应用程序的感觉虽然算不上具有革命性,但确实要改变看待问题的方式。事实上,只要多一些独创性和预见性,你就能做到以最小的努力、最低的成本和最少的时间投入,开发出具有桌面应用程序体验的Web应用。不过前提是要理解浏览器如何与人进行交互。
事件就是操作检测与脚本执行的组合,或者基于检测到的操作类型并在某个对象上调用事件侦听器。首先,需要澄清几个术语:
□ 事件:这里需要澄清的是事件并不是以”on”开头的。例如,onclick不是事件,click才是事件。
□ 事件侦听器:当指定的事件发生时会执行的函数或方法。有时也被称为“事件处理程序”,“事件侦听器”是遵循DOM2级事件规范中的术语。
□ 事件注册:为DOM元素的具体事件指定事件侦听器的过程。注册可以通过几种不同的方式来完成。
□ 调用:使用动词“invoke”来描述浏览器在检测到某种操作之后执行相应事件侦听器的情形。类似的术语还可能包括呼叫(called)、激发(fired)、执行(executed)或触发(triggered)等。
事件类型
事件可以分为几种类型:对象事件、鼠标事件、键盘事件、表单事件、W3C事件以及针对浏览器的事件。
对象事件
对象事件既适用于JavaScript对象(例如window对象),也适用于DOM对象(例如HTMLImageElement对象)
1. load和unload事件
浏览器会在完成对页面的载入时调用window的load事件。通过借助于load事件,可以使不唐突的行为增强只有在JavaScript有效时才会应用到页面上。同样地,当用户通过单击链接或者关闭窗口而即将离开当前页面时,会调用window对象的unload事件。因此,可以通过unload事件侦听器在页面被关闭之前捕获最后一瞬间的信息。
2. abort和error事件
与load和unload事件类似,error事件既适用于window对象,也适用于图像对象。error事件在动态载入图像时,可以用来识别图像载入错误。如果创建了一个新的img DOM要素,那么就可以使用load事件侦听器在且只在图像载入成功的情况下再将这个DOM元素添加到文档中。同样,对于载入图像时出现错误的情况,可以使用error事件侦听器来进行说明,并采取适当的行动。
1 | ADS.addEvent(window, 'load' , function () { |
3 | var image = document.createElement( 'IMG' ); |
5 | ADS.addEvent(image, 'load' , function () { |
6 | document.body.appendChild(image); |
9 | ADS.addEvent(image, 'error' , function () { |
10 | var message = document.createTextNode( 'The image failed to load' ); |
11 | document.body.appendChild(message); |
14 | image.setAttribute( 'src' ,images/working.jpg'); |
在图像载入成功的情况下,它会被添加到文档主体中;但如果载入失败,则会看到出错信息。
至于abort事件,它的作用很小。只有在图像完全载入之前,因为浏览器停止载入页面而导致图像载入失败的情况下,才会调用这个事件。而这种情况通常只在单击浏览器“停止”按钮是才会发生。
3. resize事件
当调整浏览器窗口的大小而导致文档的视图发生改变时才会触发resize事件。
4. scroll事件
scroll事件适用于具有overflow:auto样式的元素,并且会在元素滚动期间连续地触发。可能引起事件的操作包括:拖动滚动条、滚动鼠标滚轮、按下键盘的方向键或其他滚动操作。
鼠标移动事件
□ 当鼠标处于移动过程中时,鼠标指针下方的对象就会连续地触发mousemove事件
□ 当指针移动到一个新对象上面时则会触发mouseover事件
□ 当指针移出对象时会触发mouseout事件
示例文件由一个非常简单的HTML文件和相应的JavaScript脚本文件组成,包括完整的自定义ADS库,一个myLogger日志对象,以及load事件侦听文件。
效果演示
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
5 | < title >Mouse Move Events</ title > |
6 | < link rel = "stylesheet" type = "text/css" href = "style.css" /> |
8 | < script type = "text/javascript" src = "libs/ADS-final-verbose.js" ></ script > |
9 | < script type = "text/javascript" src = "libs/myLogger-final/myLogger.js" ></ script > |
10 | < script type = "text/javascript" src = "move.js" ></ script > |
13 | < h1 >Mouse Move Events</ h1 > |
15 | < div id = "box" > Test Box </ div > |
在move.js脚本文件中注册了所有鼠标移动事件:
1 | ADS.addEvent(window, 'load' , function (W3CEvent) { |
3 | function logit(W3CEvent) { |
4 | switch ( this .nodeType) { |
5 | case ADS.node.DOCUMENT_NODE: |
6 | ADS.log.write(W3CEvent.type + ' on the document' ); |
8 | case ADS.node.ELEMENT_NODE: |
9 | ADS.log.write(W3CEvent.type + ' on the box' ); |
14 | ADS.addEvent(document, 'mousemove' ,logit); |
15 | ADS.addEvent(document, 'mouseover' ,logit); |
16 | ADS.addEvent(document, 'mouseout' ,logit); |
18 | var box = document.getElementById( 'box' ); |
19 | ADS.addEvent(box, 'mousemove' ,logit); |
20 | ADS.addEvent(box, 'mouseover' ,logit); |
21 | ADS.addEvent(box, 'mouseout' ,logit); |
下图展示了鼠标指针从左边移动到右边的事件:

鼠标单击事件
□ 当用户在页面上单击鼠标时,在发生鼠标移动事件的同时还会启动另一条事件链:
□ 当在对象上方按下鼠标按键时触发mousedown事件
□ 但释放鼠标按键时触发mouseup事件
□ 只有在鼠标保持不动时才触发click事件
□ 如果快速按两次按键,则会在click事件后发生dblclick事件
需要注意的是,只有当鼠标指针保持不动的情况下,以上的顺序才是正确的。下面的示例和move.js非常类似,只是修改了事件侦听部分的代码。为document和box元素注册的是click、dblclick、mousedown和mouseup。
效果演示
1 | ADS.addEvent(window, 'load' , function (W3CEvent) { |
3 | function logit(W3CEvent) { |
4 | switch ( this .nodeType) { |
5 | case ADS.node.DOCUMENT_NODE: |
6 | ADS.log.write(W3CEvent.type + ' on the document' ); |
8 | case ADS.node.ELEMENT_NODE: |
9 | ADS.log.write(W3CEvent.type + ' on the box' ); |
14 | ADS.addEvent(document, 'mousedown' ,logit); |
15 | ADS.addEvent(document, 'mouseup' ,logit); |
16 | ADS.addEvent(document, 'click' ,logit); |
17 | ADS.addEvent(document, 'dblclick' ,logit); |
19 | var box = document.getElementById( 'box' ); |
20 | ADS.addEvent(box, 'mousedown' ,logit); |
21 | ADS.addEvent(box, 'mouseup' ,logit); |
22 | ADS.addEvent(box, 'click' ,logit); |
23 | ADS.addEvent(box, 'dblclick' ,logit); |
下图在示例文件的页面上单击并释放鼠标指针不会触发click事件的示意图:

它会在ADS.log对象创建的日志窗口中输出以下两个事件:
(1) document对象上的mousedown事件
(2) document对象上的mouseup事件
在单击和释放鼠标之间,由于拖动了鼠标指针,所以不会发生click事件:
而下图在页面上单击并在box元素上释放鼠标指针

会输出以下事件:
(1) document对象上的mousedown事件
(2) box对象上的mouseup事件
(3) document对象上的mouseup事件
之所以在box上面触发mouseup事件,是因为在box上面释放了鼠标键。虽然mousedown和mouseup是相关的,但它们并不一定成对触发。
键盘事件
当某个键被按下时,浏览器调用事件的顺序与click事件相同,只不过都是按键有关的事件:
(1) keydown:按下某个键时触发
(2) keyup:释放某个键时触发
(3) keypress:在keyup事件之后触发,表示按下了某个键
表单事件
1. submit和reset事件
submit事件:单击提交按钮,或者按下键盘中的某个键将表单提交到服务器时触发
reset事件:重置表单时触发
利用submit和reset事件可以进行客户端表单验证等操作。
效果演示
下面的示例是一个HTML表单:
1 | < form action = "" method = "post" id = "canadianAddress" > |
3 | < label for = "name" >Name</ label > |
4 | < input type = "text" id = "name" name = "name" /> |
7 | < label for = "postalCode" >Postal Code (required)</ label > |
8 | < input type = "text" id = "postalCode" name = "postalCode" class = "required" /> |
9 | < p >In the format A#A #A#</ p > |
12 | < label for = "street" >Street</ label > |
13 | < input type = "text" id = "street" name = "street" /> |
16 | < label for = "city" >City</ label > |
17 | < input type = "text" id = "city" name = "city" /> |
20 | < label for = "province" >Province</ label > |
21 | < input type = "text" id = "province" name = "province" /> |
24 | < input type = "submit" value = "submit" /> |
为了验证表单邮政编码格式的正确性,本例中包含了一个submit事件侦听器:
1 | function isPostalCode(s) { |
2 | return s.toUpperCase().match( /[A-Z][0-9][A-Z]\s*[0-9][A-Z][0-9]/i ); |
5 | ADS.addEvent(window, 'load' , function () { |
7 | document.getElementById( 'canadianAddress' ), |
10 | var postalCode = document.getElementById( 'postalCode' ).value; |
13 | if (!isPostalCode(postalCode)) { |
14 | alert( 'That\'s not a valid Canadian postal code!' ); |
17 | ADS.preventDefault(W3CEvent); |
当邮政编码的格式填写不正确时,submit事件会提示错误信息:

2. blur和focus事件
focus事件:用户单击一个表单元素,或者通过tab键切换到一个表单元素时触发
blur事件:单击表单元素之外的区域,或者通过tab键离开该表单元素时触发
下面的示例利用了focus和blur事件来切换样式,从视觉上表示表单元素的填写状态:
1 | ADS.addEvent(window, 'load' , function () { |
3 | var postalCode = document.getElementById( 'postalCode' ); |
4 | postalCode.className = 'inputMissing' ; |
7 | ADS.addEvent(postalCode, 'focus' , function (W3CEvent) { |
9 | this .className = 'inputEditing' ; |
13 | ADS.addEvent(postalCode, 'blur' , function (W3CEvent) { |
14 | if ( this .value == '' ) { |
16 | this .className = 'inputMissing' ; |
17 | } else if (!isPostalCode( this .value)) { |
19 | this .className = 'inputInvalid' ; |
22 | this .className = 'inputComplete' ; |
这些事件侦听器通过修改文本框的className属性,实现了文本框的样式随着获得和失去交点而改变。当编辑邮政编码时,触发focus事件修改相应的样式:

当离开邮政编码框时,浏览器会触发blur事件,同时事件侦听器检测文本框中是否包含有效的信息,并重新应用适当的样式:

3. change事件
change事件:触发focus事件之后,在focus和blur事件之间修改元素的值是触发。
例如,当填写完邮政编码之后,change事件会向服务器端脚本发送一个XMLHttpRequest请求,利用服务器端的服务来验证邮政编码的格式。与此同时,服务器端脚本可以查询街道、城市以及省份的信息,并自动生成表单中其它字段的信息:
1 | ADS.addEvent(window, 'load' , function () { |
2 | var postalCode = ADS.$( 'postalCode' ); |
4 | ADS.addEvent(postalCode, 'change' , function (W3CEvent) { |
6 | var newPostalCode = this .value |
8 | if (!isPostalCode(newPostalCode)) return ; |
10 | var req = new XMLHttpRequest(); |
11 | req.open( 'POST' , 'server.js?postalCode=' + newPostalCode, true ); |
12 | req.onreadystatechange = function () { |
13 | if (req.readyState == 4) { |
14 | eval(req.responseText); |
16 | if (ADS.$( 'street' ).value == '' ) { |
17 | ADS.$( 'street' ).value = street; |
19 | if (ADS.$( 'city' ).value == '' ) { |
20 | ADS.$( 'city' ).value = city; |
22 | if (ADS.$( 'province' ).value == '' ) { |
23 | ADS.$( 'province' ).value = province; |
这里引用的服务器端脚本server.js,也只是一个包含街道、城市以及省份信息的文件:
1 | var street = '123 Somewhere' ; |
3 | var province = 'Ontario' ; |
最终的结果是既验证了邮政编码信息的真实性,同时又获得了街道、城市以及省份信息,如下图所示:

转载请注明:陈童的博客 » ADS4.1 响应用户操作和事件——事件的类型