事件对象
键盘与文本事件
用户在使用键盘时会触发键盘事件。有3个键盘事件,如下:
- keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
- keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
- keyup:当用户释放键盘上的键时触发。
只有一个文本事件:textInput。这个事件是对keypress的补充,用意是在将文本显示给用户之前更容易拦截文本。在文本插入文本框之前会触发textInput事件。
键码:在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键(键码)对应。对于数字字母字符键,keyCode属性的值与ASCII码中对应小写字母或数字的编码相同。
字符编码:IE9、Firefox、Chrome和Safari的event对象都支持一个charCode属性,这个属性只有在发生keypress事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII编码。此时的keyCode通常等于0或者也可能等于所按键的键码。IE8及之前版本和Opera则是在keyCode中保存的ASCII编码。
DOM3级变化:DOM3级事件中的键盘事件,不再包含charCode属性,而是包含两个新属性:key和char。而Safari5和Chrome支持名为keyIdentifier的实现。DOM3级事件还添加了一个名为location的属性,这是一个数值,表示按下了什么位置上的键。Safari和Chrome支持名为keyLocation的等价属性。由于这几个属性都存在跨浏览器问题,所以不推荐使用。event对象添加了getModifierState()方法,接收一个参数,即等于Shift、Control、AltGraph或Meta的字符串,表示要检测的修改键。如果指定的修改键时活动的(也就是处于被按下的状态),和这个方法返回true,否则返回false。IE9是唯一支持这个方法的浏览器。
textInput事件:“DOM3级事件”引入的新事件。当用户在可编辑区域中输入字符时,就会触发这个事件。它的event对象中包含一个data属性,这个属性的值就是用户输入的字符(非字符编码)。还有一个属性inputMethod,表示把文本输入到文本框中的方式。支持textInput属性的浏览器有IE9+、Safari和Chrome。只有IE支持inputMethod属性。
设备中的按键事件:任天堂Wii会在用户按下Wii遥控器上的按键时触发键盘事件。
复合事件
复合事件是DOM3级事件中新添加的一类事件,用于处理IME的输入序列。IME(输入法编辑器)可以让用户输入在物理键盘上找不到的字符。IME通常需要同时按下多个键,但最终只输入一个字符。复合事件就是针对检测和处理这种输入而设计的。IE9+是到2011年唯一支持复合事件的浏览器。由于缺少支持,对于需要开发跨浏览器应用的开发人员,它的用处不大。
变动事件
DOM2级变动事件能在DOM中的某一部分发生变化时给出提示。变动事件是为XML或HTML DOM设计的,并不特定于某种语言。主要变动事件如下:
- DOMSubtreeModified:在DOM结构中发生任何变化时触发。这个事件在其它事件触发后都会触发。
- DOMNodeRemoved:在节点从其父节点中移出时触发。(删除节点时)
- DOMNodeInserted:在一个节点作为另一个节点的子节点插入时触发。(插入节点)
- DOMNodeInsertedIntoDocument:在一个节点直接被插入到文档中或通过DOM子树结构插入到文档中时触发。
- DOMNodeRemovedFromDocument:在一个节点直接从文档中移出或通过DOM子树结构移出时触发。
删除节点:在使用removeChild()或replaceChild()方法从DOM中删除节点时。首先会触发DOMNodeRemoved(删除节点时会触发)事件,该事件的目标(event.target)是被删除的节点,而event.relatedNode属性包含着对目标节点的父节点的引用。这个事件会冒泡,因此可以在DOM结构的任何层次上处理。如果被删除的节点包含子节点(被删除的节点有子节点),就会在其所有子节点上以及该节点上相继触发DOMNodeRemovedFromDocument事件,该事件不会冒泡。这个事件的目标(event.target)是相应的子节点或被移出的这个节点。除此之外event对象不包含其它信息。最后会触发DOMSubtreeModifined事件,这个事件引用的是被删除节点的父节点。除此之外event对象不包含任何其它事件信息。
插入节点:
在使用appendChild()、insertBefore()、replaceChild()方法向DOM中插入节点时,首先
会触发DOMNodeInsered事件,
该事件的
目标是被插入的节点(event.target)
,而event.relatedNode包含一个父节点的引用。在
这个事件触发时,该节点已经被插入到文档中了
。
这个事件有冒泡,所有可以DOM的任何层次上处理。
接着会在新插入的节点上触
发DOMNoeInseredIntoDocument
事件。该事件不会冒泡,所有需在该节点被插入之前为它添加事件处理程序(
也就是说只能新插入的节点上创建处理程序
),该事件的目标就是这个新插入的节点,除此之外Event对象不包含其它事件信息。
最后在新插入的节点的父节点上触发DOMSubtreeModefied事件。
HTML5事件
DOM规范没有涵盖浏览器支持的所有事件,HTML5列出了浏览器的支持的所有事件,下面将介绍得到浏览器完美支持的事件
contextmenu事件:用以表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。这个事件是冒泡的,因此可以为document指定一个事件处理程序,用以处理页面发生的所有此类事件。这个事件的目标是发生用户操作的元素。
通常使用contextmenu事件来显示自定义的菜单栏,而通过鼠标的click事件来隐藏该菜单栏。
beforeunload事件:为了让开发人员有可能在页面卸载前阻止这一操作。这个事件会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面。
DOMContentLoaded事件:该
事件在形成完整的DOM结构之后就会触发,而不必等到图像、JavaScript资源、CSS资源加载完毕之后才触发。这样用户就可以早点与浏览器交互。
可以为document或window添加处理程序,但目标还是document
。通常这个事件既可以添加事件处理程序,也可以执行其他DOM操作。这个事件始终都会在load事件之前触发。
readystatechange事件:
该事件的目的是提供
与文档或元素加载状态有关
的信息,但这个事件的行为有时又很难预料。该事件的Event对象有个readyState属性,保存着状态值。状态值有5个:①
uninitialized(未初始化):对象存在但未初始化;
②loading(正在加载):对象正在加载数据;
③loaded(加载完毕):对象已经加载完数据;
④interactive(交互):可以操作对象了,但未完全加载;
⑤compelete(完成):对象已经加载完毕。在与load事件一起使用时,无法预测两个事件的先后顺序。而且交互阶段可能会早于也可能会晚于完成阶段出现。
pageshow事件和pagehide事件:
这两个事件的目标是document,但必需添加到window对象上。
Firefox和Opera
有一个特性叫做"往返缓存",
往返缓存(bfcache)可以在用户使用浏览器的"后退"和"前进"按钮时加快页面的转换速度
。bfcache中不仅保存了页面的数据,还保存了DOM和JavaScript的状态。将整个页面保存在了内存中,如果页面是在bfcache中,那么打开这个页面时,不会触发load事件。
pageshow事件
会在页面显示时触发,无论该页面是在bfcahce中,还是触发了load事件。
pageshow事件会在load事件触发之后触发
。
pageshow事件的Event对象中有个
叫persisted的属性
,该属性是一个布尔值,true表示页面被保存在bfcache中,否则,这个属性值为false。
与pageshow事件相对的就是
pagehide事件
,该事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发。该事件也有一个persisted属性,如果页面在卸载之后会被保存在bfcache中,该属性就会被设置为true,否则为false。
hashchange事件:该事件以便在URL的参数列表发生变化时通知开发人员。必须要把这个事件处理程序添加给window对象,然后URL参数列表只要变化就会调用它。
Event对象中有两个属性:oldURL和newURL。分别表示变化前的URL和变化后的URL。通常用于Ajax技术。
设备事件
设备事件可以让开发人员确定用户在怎样使用设备。
orientationchange事件:苹果公司为移动Safari中添加了该事件,以便开发人员能够确定用户何时将设备由横向查看模式切换为纵向查看模式。移动Safari的window.orientation属性中可能包含3个值:0表示肖像模式,90表示向左旋转的横向模式(主屏幕按钮在右侧),-90表示向右旋转的横向模式(主屏幕在左侧)。触发该事件event对象不包含任何有价值的信息。
MozOrientation事件:该事件只能提供一个平面的方向变化,该事件在window对象上触发,此时event对象包含3个属性:x、y和z。这几个属性的值都介于1到-1,表示不同坐标轴上的方向。只有带加速计的设备才支持该事件。
deviceorientation事件:该事件的意图是告诉开发人员设备在空间中朝向哪儿,而不是如何移动。设备在三维空间是靠x、y和z轴来定位的。
devicemotion事件:这个事件是要告诉开发人员设备什么时候移动,而不仅仅是设备方向如何变化。
触摸与手势事件
触摸事件:该事件会在用户手指放在屏幕上时、在屏幕上滑动或从屏幕上移开时触发。主要触摸事件如下:
- touchstart:当手指触摸屏幕时触发;即使已经有一个手指放在了屏幕上也会触发。
- touchmove:当手指在屏幕上滑动时连续地触发。在这个事件发生期间,调用preventDefault()可以阻止滚动。
- touchend:当手指从屏幕上移开时触发。
上面的几个事件都会冒泡,也都可以取消。每个触摸事件的event对象都提供了在鼠标事件中常见的属性以及3个用于跟踪触摸的属性:touches、targetTouchs和changeTouches。
手势事件:当两个手指触摸屏幕时就会产生手势,手势通常会改变显示项的大小,或者旋转显示项。有3个手势事件如下:
- gesturestart:当一个手指已经按在屏幕上而另一个手指又触摸屏幕时触发。
- gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。
- gestureend:当任何一个手指从屏幕上面移开时触发。
这些事件都都会冒泡。只有两个手指都触摸到事件的接收容器时才会触发这些事件。此时,事件的目标就是两个手指都位于其范围内的那个元素。每个手势事件的event对象都包含着标准的鼠标事件属性,此外还包括两个额外的属性:rotation和scale。其中,rotation属性表示手指变化引起的旋转角度,负值表示逆时针旋转,正值表示顺时针旋转(该值从0开始)。而scale属性表示两个手指间距离的变化情况(例如向内收缩会缩短距离);这个值从1开始,并随距离拉大而增长,随距离缩短而减小。
内存与性能
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。首先,每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
事件委托
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。所有用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术。
如果可行的话,可以考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件。这样做比采取传统的做法有很多好处。
最适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress。虽然mouseover和mouseout事件也冒泡,但要适当处理它们并不容易,而且经常需要计算元素的位置。(因为当鼠标从一个元素移到其子节点时,或者当鼠标移出该元素时,都会触发mouseout事件。)
移除事件处理程序
在不需要的时候移除事件处理程序,也是解决这个问题的一种方案。内存中留有那些过时不用的“空事件处理程序”,也是造成web应用程序内存与性能问题的主要原因。
在两种情况下,可能会造成上述问题:第一种就是从文档中移除带有事件处理程序的元素时,如果你知道某个元素即将被移除,那么最好手工移除事件处理程序(注意:在事件处理程序中删除按钮也能阻止事件冒泡。目标元素在文档中是事件冒泡的前提)。第二种就是卸载页面的时候,一般来说,最好的做法是在页面卸载之前,先通过onunload事件处理程序移除所有事件处理程序。
模拟事件
事件,就是网页中某个特别值得关注的瞬间。事件经常由用户操作或通过其他浏览器功能来触发。但很少有人知道,也可以使用JavaScript在任意时刻来触发特定的事件,而此时的事件就如同浏览器创建的事件一样。也就是说,这些事件该冒泡还会冒泡,而且照样能够导致浏览器执行已经指定的处理它们的事件处理程序。在测试web应用程序,模拟触发事件是一种极其有用的技术。
DOM中的事件模拟
可以在document对象上使用creatEvent()方法创建event对象。这个方法接收一个参数,即表示要创建的事件类型的字符串。如:UIEvents、MouseEvents、MutationEvents和HTMLEvents。
在创建了event对象之后,还需要使用与事件有关的信息对其进行初始化。每种类型的event对象都有一个特殊的方法,为它传入适当的数据就可以初始化该event对象。不同类型的这个方法的名字也不相同,具体要取决于createEvent()中使用的参数。
模拟事件的最后一步就是触发事件。这一步需要使用dispatchEvent()方法,所有支持事件的DOM节点都支持这个方法。调用dispatchEvent()方法时,需要传入一个参数,即表示要触发事件的event对象。
IE中的事件模拟
调用document.createEventObject()方法可以在IE中创建event对象。然后,必须手工为这个对象添加所有必要的信息(没有方法来辅助完成这一步骤)。最后一步就是在目标上调用fireEvent()方法,这个方法接收两个参数:事件处理程序的名称和event对象。在调用fireEvent()方法时,会自动为event对象添加srcElement和type属性;其他属性则都是必须通过手工添加的。