JavaScript事件 DOMNodeInserted DOMNodeRemoved
JavaScript与HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定交互的瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。
13.1 事件流
事件流描述的是从页面中接收事件的顺序。
1.3.1.1 事件冒泡
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
13.1.2 事件捕获
事件捕获(event capturing)的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。
13.1.3 DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
13.2 事件处理程序
事件就是用户或浏览器自身执行某种动作。诸如click、load和mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以“on”开头。
13.2.1 HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的JavaScript代码。
事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。
13.2.2 DOM0级事件处理程序
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
使用DOM0级方法指定事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行;换句话说,程序中的this引用当前元素。
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
也可以删除通过DOM0级方法指定的事件处理程序,只要将事件处理程序属性的值设置为null即可。
如果你使用HTML指定事件处理程序,那么onclick属性的值就是一个包含着同名HTML特性中指定的代码的函数。而将相应的属性设置为null,也可以删除以这种方式指定的事件处理程序。
13.2.3 DOM2级事件处理程序
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有的DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值
参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
与DOM0级方法一样,这里添加的事件处理程序也是在其依附的元素作用域中运行。使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventKListener()添加的匿名函数将无法移除。
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。
IE9、Firefox、Safari、Chrome和Opera支持DOM2级事件处理程序。
13.2.4 IE事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。这个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于IE8及早更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域。在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域运行,因此this等于window。
与DOM不同的是,这些事件处理程序不是以添加它们的顺序执行的,而是以想法的顺序被触发。
使用attachEvent()添加的事件可以通过detachEvent()来移除,条件是必须提供相同的参数。与DOM方法一样,这也意味着添加的匿名函数将不能被移除。
支持IE事件处理程序的浏览器有IE和Opera。
13.2.5 跨浏览器的事件处理程序
要保证处理事件的代码能在大多数浏览器下一致地运行,只需关注冒泡阶段。
13.3 事件对象
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。
13.3.1 DOM中的事件对象
兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。
event对象包含与创建它的特定事件有关的属性和方法。
属性/方法 |
类型 |
读/写 |
说明 |
bubbles |
Boolean |
只读 |
表明事件是否冒泡 |
cancelable |
Boolean |
只读 |
表明是否可以取消事件的默认行为 |
currentTarget |
Element |
只读 |
其事件处理程序当前正在处理事件的那个元素 |
defaultPrevented |
Boolean |
只读 |
为true表示已经调用了preventDefault()(DOM3级事件中新增) |
detail |
Integer |
只读 |
与事件相关的细节信息 |
evnetPhase |
Integer |
只读 |
调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段 |
preventDefault() |
Function |
只读 |
取消事件的默认行为。如果cancelable是true,则可以使用这个方法 |
stopImmediatePropagation() |
Function |
只读 |
取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用(DOM3级事件中新增) |
stopPropagation() |
Function |
只读 |
取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法 |
target |
Element |
只读 |
事件的目标 |
trusted |
Boolean |
只读 |
为true表示事件是浏览器生成的。为false表示事件是由开发人员通过JavaScript创建的(DOM3级事件中新增) |
type |
String |
只读 |
被触发的事件的类型 |
view |
AbstractView |
只读 |
与事件关联的抽象视图。等同与发生事件的window对象 |
在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标,如果直接将事件处理程序指定给了目标元素,则this、currentTarget和target包含相同的值。
要阻止特定事件的默认行为,可以使用preventDefault()方法。
只有cancleable属性设置为true的事件,才可以使用preventDefault()来取消其默认行为。
事件对象的eventPhase属性,可以用来确定事件当前正位于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么,eventPhase等于1,;如果事件处理程序处于目标对象上,则eventPhase等于2;如果是在冒泡阶段调用的事件处理程序,eventPhase等于3。这里要注意的是,尽管“处于目标”发生在冒泡阶段,但eventPhase仍然一直等于2。
13.3.2 IE中的事件对象
在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。如果事件处理程序是使用attachEvent()添加的,那么就会有一个event对象作为参数被传入事件处理程序函数中。
如果是通过HTML特性指定的事件处理程序,那么还可以通过一个名叫evnet的变量来访问event对象(与DOM中的事件模型相同)。
属性/方法 |
类型 |
读/写 |
说明 |
cancelBubble |
Boolean |
读/写 |
默认值为false,但将其设置为true就可以取消事件冒泡(与DOM中的stopPropagation()方法的作用相同) |
returnValue |
Boolean |
读/写 |
默认值为true,但将其设置为false就可以取消事件的默认行为(与DOM中的preventDefault()方法的作用相同) |
srcElement |
Element |
只读 |
事件的目标(与DOM中的target属性相同) |
type |
String |
只读 |
被触发的事件的类型 |
13.3.3 跨浏览器的事件对象
13.4 事件类型
13.4.1 UI事件
UI事件指的是那些不一定与与用户操作有关的事件。这些事件在DOM规范出现之前,都是以这种或那种形式存在的,而在DOM规范中保留是为了向后兼容。现有的UI事件如下:
(1)DOMActivate:表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在DOM3级事件中被废弃,但Firefox2+和Chrome支持它。考虑到不同浏览器实现的差异,不建议使用这个事件。
(2)load:当页面完全加载后在window上面触发,当所有框架都加载完毕时在框架上面触发,当图像都加载完毕时在<img>元素上面触发,或者当嵌入的内容加载完毕时在<object>元素上面触发。
(3)unload:当页面完全卸载后在window上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在<object>元素上面触发。
(4)abort:当用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上面触发。
(5)error:当发生JavaScript错误时在window上面触发,当无法加载图像时在<img>元素上面触发,当无法加载嵌入内容时在<object>元素上面触发,或者当有一或多个框架无法加载时在框架集上面触发。
(6)select:当用户选择文本框(<input>或<textarea>)中的一或多个字符时触发。
(7)resize:当窗口或框架的大小变化时在window或框架上面触发。
(8)scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加载页面的滚动条。
注意:根据“DOM2级事件”规范,应该在document而非window上面触发load事件。但是,所有浏览器都在window上面实现了该事件,以确保向后兼容。
注意:新图像元素不一定要从添加到文档后才开始下载,只要设置了src属性就会开始下载。
与图像不同,只有在设置了<script>元素的src属性并将该元素添加到文档后,才会开始下载JavaScript文件。
与<script>节点类似,在未指定href属性并将<link>元素添加到文档之前也不会开始下载样式表。
关于何时会触发resize事件,不同浏览器有不同的机制。IE、Safari、Chrome和Opra会在浏览器窗口变化了1像素时就触发resize事件,然后随着变化不断重复触发。Firefox则只会在用户停止调整浏览器大小时才会触发resize事件。
13.4.2 焦点事件
焦点事件会在页面获得或失去焦点时触发。利用这些事件并与document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪。有一下6个焦点事件。
(1)blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
(2)DOMFocusIn:在元素获得焦点时触发。这个事件与HTML事件focus等价,但它冒泡。只有Opra支持这个事件。DOM3级事件废弃了DOMFocusIn,选择了focusin。
(3)DOMFocusOut:在元素失去焦点时触发。这个事件是HTML事件blur的通用版本。只有Opra支持这个事件。DOM3级事件废弃了DOMFocusOut,选择了focusout。
(4)focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
(5)focusin:在元素获得焦点时触发。这个事件与HTML事件focus等价,但它冒泡。支持这个事件的浏览器有IE5.5+、Safari5.1+、Opra11.5+和Chrome。
(6)focusout:在元素失去焦点时触发。这个事件是HTML事件blur的通用版本。支持这个事件的浏览器有IE5.5+、Safari5.1+、Opra11.5+和Chrome。
当焦点从页面中的一个元素移动到另一个元素,会依次触发下列事件:
(1)focusout 在失去焦点的元素上触发
(2)focusin 在获得焦点的元素上触发
(3)blur 在失去焦点的元素上触发
(4)DOMFocusOut 在失去焦点的元素上触发
(5)focus 在获得焦点的元素上触发
(6)DOMFocusIn 在获得焦点的元素上触发
13.4.3 鼠标与滚轮事件
DOM3级事件中定义了9个鼠标事件,简介如下。
(1)click:在用户单机主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。
(2)dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。
(3)mousedown:在用户按下了任意鼠标按钮时触发。
(4)mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
(5)mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
(6)mousemove:当鼠标指针在元素内部移动时重复地触发。
(7)mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。
(8)mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。
(9)mouseup:在用户释放鼠标按钮时触发。
1.客户区坐标位置
鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的clientX和clientY属性中。
2.页面坐标位置
通过客户区坐标能够知道鼠标是在视口中什么位置发生的,而页面坐标通过事件对象的pageX和pageY属性,能告诉你事件是在页面中的什么位置发生的。换句话说,这两个属性表示鼠标光标在页面中的位置,因此坐标是从页面本身而非视口的左边和顶边计算的。
在页面没有滚动的情况下,pageX和pageY的值与clientX和clientY的值相等。
3.屏幕坐标位置
鼠标事件发生时,不仅有相对于浏览器窗口的位置,还有一个相对于整个电脑屏幕的位置。而通过screenX和screenY属性就可以确定鼠标事件发送时鼠标指针相对于整个屏幕的坐标信息。
4.修改键
虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。
DOM为此规定了4个属性,表示这些修改键的状态:shiftKey、ctrlKey、altkey和metaKey。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为true,否则为false。
5.相关元素
6.鼠标按钮
对于mousedown和mouseup事件来说,在其event对象存在一个button属性,表示按下或释放的按钮。DOM的button属性可能有如下3个值:0表示主鼠标按钮,1表示中间的鼠标按钮(鼠标滚轮按钮)2表示次鼠标按钮。
7.更多的事件信息
8.鼠标滚轮事件
与mousewheel事件对应的event对象除包含鼠标的所有标准信息外,还包含一个特殊的wheelDelta属性。当用户向前滚动鼠标滚轮时,wheelDelta是120的倍数;当用户向后滚动鼠标滚轮时,wheelDelta是-120的倍数。
9.触摸设备
10.无障碍性问题
13.3.4 键盘与文本事件
有3个键盘事件,简述如下:
(1)keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
(2)keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
(3)keyup:当用户释放键盘上的键时触发。
只有一个文本事件:textInput。这个事件是对keypress的补充,用意是在将文本显示给用户之前更容易拦截文本。在文本插入文本框之前会触发textInput事件。
键盘事件与鼠标事件一样,都支持相同的修改键。
1.键码
在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键对应。
2.字符编码
发生keyprees事件意味着按下的键会影响到屏幕中文本的显示。在所有浏览器中,按下能够输入或删除字符的键都会触发keypress事件;
IE9、Firefox、Chrome和Safari的event对象都支持一个charCode的属性,这个属性只有在发生keypress事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII编码。此时的keyCode通常等于0或者也可能等于所按键的键码。
3.DOM3级变化
DOM3级事件中的键盘事件,不再包含charCode属性,而是包含两个新属性:key和char。
4.textInput事件
与keypress的不同:(1)只有可编辑区域才能触发textInput(2)只会在用户按下能够输入的实际字符的键才会被触发。
5.设备中的键盘事件
13.4.5 复合事件
复合事件(composition event)是DOM3级事件中新添加的一类事件,用于处理IME的输入序列。IME(Input Method Editor,输入法编辑器)可以让用户输入在物理键盘上找不到的字符。复合事件就是针对检测和处理这种输入而设计的。
(1)compositionstart:在IME的文本复合系统打开时触发,表示要开始输入了。
(2)compositionupdate:在向输入字段中插入新字符时触发。
(3)compositionend:在IME的文本复合系统关闭时触发,表示返回正常键盘的输入状态。
13.6 变动事件
DOM2级的变动(mutation)事件能在DOM中的某一部分发生变化时给出提示。变动事件是为XML或HTML DOM设计的,并不特定于某种语言。DOM2级定义了如下变动事件:
(1)DOMSubtreeModified:在DOM结构中发生的任何变化时触发。这个事件在其他任何事件触发后都会触发。
(2)DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中时触发。
(3)DOMNodeRemoved:在节点从其父节点中被移除时触发。
(4)DOMNodeInsertedIntoDocument:在一个节点被直接插入文档或通过子树间接插入到文档之后触发。这个事件在DOMNodeInserted之后触发。
(5)DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。这个事件在DOMNodeRemoved之后触发。
(6)DOMAttrModified:在特性被修改之后触发。
(7)DOMCharacterDataModified:在文本节点的值发生变化时触发。
13.4.7 HTML5事件
1.contextmenu事件
2.beforeunload事件
3.DOMContentLoad事件
4.readystatechange事件
支持readystatechange事件的每个对象都有一个readyState属性,可能包含下列5个值中的一个。
(1)uninitialized(未初始化):对象存在但尚未初始化。
(2)loading(正在加载):对象正在加载数据。
(3)loaded(加载完毕):对象加载数据完成。
(4)interactive(交互):可以操作对象了,但还没有完全加载。
(5)complete(完成):对象已经加载完毕。
这些状态看起来很直观,但并非所有对象都会经历readyState的这几个阶段。换句话说,如果某个阶段不适用某个对象,则该对象完全可能跳过该阶段;并没有规定哪个阶段适用于哪个对象。
5.pageshow和pagehide事件
Firefox和Opera有一个特性,名叫“往返缓存”(back-forward cache,或bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。如果页面位于bfcache中,那么再次打开该页面时就不会触发load事件。
pageshow,这个事件在页面显示时触发,无论该页面是否来自bfcache。在重新加载的页面中,pageshow会在load事件触发后触发;而对于bfcache中的页面,pageshow会在页面状态完全恢复的那一刻触发。另外要注意的是,虽然这个事件的目标是document,但必须将其事件处理程序添加到window。
除了通常的属性之外,pageshow事件的event对象还包含一个名为persisted的布尔值属性。如果页面被保存在了bfcache中,则这个属性的值为true;否则这个属性的值为false。
pagehide事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发。与pageshow事件一样,pagehide在document上面触发,但其事件处理程序必须要添加到window对象。对于pagehide事件,如果页面在卸载之后会被保存在bfcache中,那么persisted的值也会被设置为true。
指定了onunload事件处理程序的页面会被自动排除在bfcache之外,即使事件处理程序是空的。原因在于,onunload最常用于撤销在onload中所执行的操作,而跳过onload后再次显示页面很可能就会导致页面不正常。
6.haschange事件
HTML5新增了haschange事件,以便在URL的参数列表(及URL中“#”号后面的所有字符串)发生变化时通知开发人员。
必须要把haschange事件处理程序添加给window对象,然后URL参数列表只要变化就会调用它。此时的event对象应该额外包含两个属性:oldURL和newURL。这两个属性分别保存着参数列表变化前后的完整URL。
13.4.8 设备事件
1.orientationchange事件
移动Safari的window.orientation属性中可能包含3个值:0表示肖像模式,90表示向左旋转的横向模式(“主屏幕”按钮在右侧),-90表示向右旋转的横向模式(“主屏幕”按钮在左侧)。
只要用户改变了设备的查看模式,就会触发orientationchange事件。此时的event对象不包含任何有价值的信息。
所有的ios设备都支持orientationchange事件和window.orientation属性。
2.MozOrientation事件
Firefox3.6为检测设备的方向引入了一个名为MozOrientation的新事件。(前缀Moz表示这是特定于浏览器开发商的事件,不是标准事件)当设备的加速计检测到设备方向改变时,就会触发这个事件。
3.deviceorientation事件
deviceorientation事件的意图是告诉开发人员设备在空间中朝向哪儿,而不是如何移动。
触发deviceorientation事件时,事件对象中包含着每个轴相对于设备静止状态下发生变化的信息。事件对象包含以下5个属性。
(1)alpha:在围绕z轴旋转时(即左右旋转时),y轴的度数差;是一个介于0到360之间的浮点数。
(2)beta:在围绕x轴旋转时(即前后旋转时),z轴的度数差;是一个介于-180到180之间的浮点数。
(3)gamma:围绕y轴旋转时(即扭转设备时),z轴的度数差;是一个介于-90到90之间的浮点数。
(4)absolute:布尔值,表示设备是否返回一个绝对值。
(5)compassCalibrated:布尔值,表示设备的指南针是否校准过。
4.devicemotion事件
devicemotion事件是要告诉开发人员设备什么时候移动,而不仅仅是设备方向如何改变。
触发devicemotion事件时,事件对象包含以下属性。
(1)acceleration:一个包含x、y和z属性的对象,在不考虑重力的情况下,告诉你在每个方向上的加速度。
(2)accelerationIncludingGravity:一个包含x、y和z属性的对象,在考虑z轴自然重力加速度的情况下,告诉你在每个方向上的加速度。
(3)interval:以毫秒表示的时间值,必须在另一个devicemotion事件触发前传入。这个值在每个事件中应该是一个常量。
(4)rotationRate:一个表示方向的alpha、beta和gamma属性的对象。
13.4.9 触摸与手势事件
1.触摸事件
触摸事件会在用户手指放在屏幕上面时、在屏幕上滑动时或从屏幕上移开时触发。具体来说,有以下几个触摸事件。
(1)touchstart:当手指触摸屏幕时触发;即使已经有一个手指放在了屏幕上也会触发。
(2)touchmove:当手指在屏幕上滑动时连续地触发。在这个事件发生期间,调用preventDefault()可以阻止滚动。
(3)touchend:当手指从屏幕上移开时触发。
(4)touchcancel:当系统停止跟踪触摸时触发。关于此事件的确切触发时间,文档中没有明确说明。
上面这几个事件都会冒泡,也都可以取消。虽然这些触摸事件没有在DOM规范中定义,但它们却是以兼容DOM的方式实现的。因此,每个触摸事件的event对象都提供了在鼠标事件中常见的属性:bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、ctrlkey和metaKey。
除了常见的DOM属性外,触摸事件还包含下列三个用于跟踪触摸的属性。
(1)touches:表示当前跟踪的触摸操作的Touch对象的数组。
(2)targetTouches:特定于事件目标的Touch对象的数组。
(3)changeTouches:表示紫上次触摸以来发生了什么改变的Touch对象的数组。
每个Touch对象包含下列属性。
(1)clientX:触摸目标在视口中的x坐标。
(2)cilentY:触摸目标在视口中的y坐标。
(3)identifier:标示触摸的唯一ID。
(4)pageX:触摸目标在页面中的x坐标。
(5)pageY:触摸目标在页面中的y坐标。
(6)screenX:触摸目标在屏幕中的x坐标。
(7)screenY:触摸目标在屏幕中的y坐标。
(8)target:触摸的DOM节点目标。
2.手势事件
iOS2.0中的Safari还引入了一组手势事件。当两个手指触摸屏幕时就会产生手势,手势通常会改变显示项的大小,或者旋转显示项。有三个手势事件,分别介绍如下。
(1)gesturestart:当一个手指以及按在屏幕上而另一个手指又触摸屏幕时触发。
(2)gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。
(3)gestureend:当任何一个手指从屏幕上面移开时触发。
只有两个手指都触摸到事件的接收容器时才会触发这些事件。
触摸事件和手势事件之间存在某种关系。当一个手指放在屏幕上时,会触发touchstart事件。如果另一个手指又放在了屏幕上,则会先触发gesturestart事件,随后触发基于该手指的touchstart事件。如果一个或两个手指在屏幕上滑动,将会触发gesturechange事件。但只要有一个手指移开,就会触发gesureend事件,紧接着又会触发基于该手指的touchend事件。
与触摸事件一样,每个手势事件的event对象都包含着标准的鼠标事件属性。此外,还包含两个额外的属性:rotation和scale。其中,rotation属性表示手指变化引起的旋转角度,负值表示逆时针旋转,正值表示顺时针旋转(该值从0开始)。而scale属性表示两个手指间距离的变化情况(例如向内会缩短距离);这个值从1开始,并随距离拉大而增长,随距离缩短而减小。
13.5 内存和性能
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。首先,每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须实现指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交换就绪时间。
13.5.1 事件委托
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
最适合采用事件委托技术的事件包括:click、mousedown、mouseup、keydown、keyup和keypress。
13.5.2 移除事件处理程序
每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的JavaScript代码之间就会建立一个连接。这种连接越多,页面执行起来就越慢。在不需要的时候移除事件处理程序,也是解决这个问题的一种方案。
注意,在事件处理程序中删除按钮也能阻止事件冒泡。目标元素在文档中是事件冒泡的前提。
一般来说,最好的做法是在页面卸载之前,先通过onunload事件处理程序移除所有事件处理程序。
13.6 模拟事件
13.6.1 DOM中的事件模拟
可以在document对象上使用createEvent()方法创建event对象。这个方法接收一个参数,即表示要创建的事件类型的字符串。在DOM2级中,所有这些字符串都是用英文复数形式,而在DOM3级中都变成了单数。这个字符串可以是下列几个字符串之一。
(1)UIEvents:一般化的UI事件。鼠标事件和键盘事件都继承自UI事件。DOM3级中是UIEvent。
(2)MouseEvents:一般化的鼠标事件。DOM3级中是MouseEvent。
(3)MutationEvents:一般化的DOM变动事件。DOM3级中是MutationEvent。
(4)HTMLEvents:一般化的HTML事件。没有对应的DOM3级事件(HTML事件被分散到其他类别中。)
在创建了event对象之后,还需要使用与事件有关的信息对其进行初始化。每种类型的event对象都有一个特殊的方法,为它传入适当的数据就可以初始化该event对象。不同类型的这个方法的名字也不相同,具体要取决于createEvent()中使用的参数。
模拟事件的最后一步就是要触发事件。这一步需要使用dispatchEvent()方法,所有支持事件的DOM节点都支持这个方法。调用dispatchEvent()方法时,需要传入一个参数,即表示要触发事件的event对象。触发事件之后,该事件就跻身“官方事件”之列了,因而能够照样冒泡并引发相应事件处理程序的执行。
13.6.2 IE中的事件模拟
在IE8及之前版本中模拟事件与在DOM中模拟事件的思路相似:先创建event对象,然后为其指定相应的信息,然后再使用该对象来触发事件。当然,IE在实现每个步骤时都采用了不一样的方式。
调用document.createEventObject()方法可以在IE中创建event对象。但与DOM方式不同的是,这个方法不接受参数,结果会返回一个通用的event对象。然后,你必须手工为这个对象添加所有必要的信息(没有方法来辅助完成这一步骤)。最后一步就是在目标上调用fireEvent()方法,这个方法接受两个参数:事件处理程序的名称和event对象。在调用fireEvent()方法时,会自动为event对象添加srcElement和type属性;其他属性则都是必须通过手工添加的。
13.7 小结
在使用事件时,需要考虑如下一些内存和性能方面的问题:
(1)有必要限制一个页面中事件处理程序的数量,数量太多会导致占用大量内存,而且也会让用户感觉页面反应不够灵敏。
(2)建立在事件冒泡机制之上的事件委托技术,可以有效减少事件处理程序的数量。
(3)建议在浏览器卸载页面之前移除页面中的所有事件处理程序
$(
"#xxx"
).bind(
"DOMNodeInserted"
,
function
() {
});
$(
"#xxx"
).bind(
"DOMNodeRemoved"
,
function
() {
});
//IE浏览器不支持
<script>
window.onload = function() {
var count = 0,
insertListener = function(event){
console.warn("Another node has been inserted! ", event);
if (event.animationName == "nodeInserted") {
event.target.textContent = "Element " + count++ + " has been injected!";
}
}
document.addEventListener("animationstart", insertListener, false); // standard + firefox
document.addEventListener("MSAnimationStart", insertListener, false); // IE
document.addEventListener("webkitAnimationStart", insertListener, false); // Chrome + Safari
// Insert a new element
setInterval(function(){
document.getElementById("parentElement").appendChild(document.createElement("li"))
}, 2000);
};
</script>
posted on 2015-12-29 14:26 zhangzongshan 阅读(6945) 评论(0) 编辑 收藏 举报