Chapter13. 事件
事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。
JavaScript 与 HTML 之间的交互是通过事件实现的。可以使用事件处理程序来预定事件,以便事件发生时执行相应的代码。
事件流
事件流描述的是从页面中接收事件的顺序。
事件冒泡
IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
所有现代浏览器支持事件冒泡,但具体实现上还是有一些区别。IE5.5 事件冒泡会跳过<html>元素。IE9+ 则将事件一直冒泡到window对象。
事件捕获
Netscape 团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获与冒泡相反,首先由不具体的节点接收,最后是最具体的节点接收事件。事件捕获的用意是在事件到达预定目标之前捕获它。
由于老版本的浏览器不支持事件捕获,因此建议使用事件冒泡,有特殊需要的时候在使用事件捕获。
DOM 事件流
“DOM2 级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生事件捕获,然后是实际目标接收到事件,最后一个阶段是事件冒泡阶段,可以在这个阶段对事件做出响应。
IE9+ 支持 DOM 事件流,而且在捕获阶段也会出发事件对象上的事件,就是有两个机会在目标对象上操作事件,IE8 及更早版本不支持。
事件处理程序
HTML 事件处理程序
通过 HTML 指定事件处理程序。
<input type="button" value="click me" onclick="showMessage()" />
这样做有几个缺点:
- 有时差,用户可能在 HTML 元素一出现在页面上就触发相应事件,但可能事件处理程序还不能执行。
- 这种事件处理程序的作用域链在不同浏览器中会导致不同结果,不同 JavaScript引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。
- HTML 与 JavaScript 代码紧密耦合,一旦改动,起码改动连个地方。不能满足结构、样式、行为分离。
DOM0 级事件处理程序
使用 JavaScript 指定事件处理程序的传统方式,就是将一个函数复制给一个事件处理程序属性。该方法简单,而且可以跨浏览器使用,知道现在依然应用广泛。
var btn = document.getElementById("myBtn");
btn.onclick = function() {
alert(this.id);
}
// 删除事件处理程序
btn.onclick = null;
DOM2 级事件处理程序
“DOM2 级时间”定义了两个方法来添加和删除事件处理程序,IE9+ 支持。
addEventListener()removeEventListener()
它们都接受 3 个参数,要处理的事件类型,作为事件处理程序的函数和一个布尔值,true表示在捕获阶段调用事件处理函数,false表示在冒泡阶段调用。
使用 DOM2 级方法可以添加多个事件处理程序。通过addEventListener()添加的事件监听必须使用removeEventListener()来移除。
IE 事件处理程序
IE 实现了与 DOM 中类似两个方法:
attachEventdetachEvent
它们都接受两个参数:事件处理程序名称与事件处理程序函数。注意attachEvent()的第一个参数是"onclick",而非 DOM 的addEventListener()的"click"。
通过attachEvent()添加的事件处理程序是在全局作用域下运行。所以this == window。此外,attachEvent添加的多个事件处理程序的触发顺序是相反的,即后添加的先触发。
跨浏览器的事件处理程序
var EventUtil = {
addHandler: function(elem, type, handler) {
if(elem.addEventListener) {
elem.addEventListener(type, handler, false);
} else if(elem.attachEvent) {
elem.attachEvent('on' + type, handler);
} else {
elem['on' + type] = handler;
}
},
removeHandler: function(elem, type, handler) {
if(elem.removeEventListener) {
elem.removeEventListener(type, handler, false);
} else if(elem.detachEvent) {
elem.detachEvent('on' + type, handler);
} else {
elem['on' + type] = null;
}
}
}
事件对象
在触发 DOM 上的某个时间时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。
DOM 中的事件对象
无论用什么方法添加事件处理程序,都会传入event对象。event对象包含与创建它的特定事件有关的属性和方法。
| 属性/方法 | 类型 | 读/写 | 说明 |
|---|---|---|---|
| bubbles | Boolean | 只读 | 表明事件是否冒泡 |
| cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为 |
| currentTarget | Element | 只读 | 表明事件处理程序当前正在处理事件的元素 |
| defaultPrevented | Boolean | 只读 | 为true表示已经调用了preventDefault()方法 |
| detail | Integer | 只读 | 与事件相关的细节信息 |
| eventPhase | Integer | 只读 | 调用事件处理程序的阶段:1 表示捕获阶段,2 表示“处于目标”阶段,3 表示冒泡阶段 |
| preventDefault() | Function | 只读 | 取消事件的默认行为 |
| stopImmediatePropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用 |
| stopPropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡 |
| target | Element | 只读 | 目标元素 |
| type | String | 只读 | 被触发的事件类型 |
| trusted | Boolean | 只读 | 为true表示事件是浏览器生成的,为false表示事件是由开发人员通过 JavaScript 创建的 |
| view | AbstractView | 只读 | 与事件相关联的抽象试图,等同于发生事件的window对象 |
在事件处理程序中,对象this始终等于currentTarget,而target只包含事件的实际目标。
只有在事件处理程序执行期间,event对象才存在。一旦时间处理程序执行完毕,event对象将会被立即销毁。
IE 中的事件对象
| 属性/方法 | 类型 | 读/写 | 说明 |
|---|---|---|---|
| cancelBubble | Boolean | 读写 | 默认为false。用于取消事件冒泡,与 DOM 中的stopPropagation()相同 |
| returnValue | Boolean | 读写 | 默认为true,用于取消默认行为,与 DOM 中的preventDeafault()相同 |
| srcElement | Element | 只读 | 事件目标元素,与 DOM 中的target相同 |
| type | String | 只读 | 被触发的事件类型 |
跨浏览器的事件对象
var eventUtil = {
addHandler: function(elem, type, handler) {
// 省略代码
},
getEvent: function(event) {
return event || window.event;
},
getTarget: function(event) {
return event.target || event.srcElement;
},
preventDefault: function(event) {
if(event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
removeHandler: function(elem, type, handler) {
// 省略代码
},
stopPropagation: function(event) {
if(event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
事件类型
UI事件
| 事件 | 说明 |
|---|---|
| load | 页面完全加载后触发 |
| unload | 页面完全卸载或触发 |
| abort | 当用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上面触发 |
| error | 当发生 JavaScript 错误时在window上触发,当无法加载图像时在<img>元素上面触发 |
| select | 用户选中文本框中的文本的时候触发 |
| resize | 窗口或框架大小变化的时候触发 |
| scroll | 滚动带滚动条的内容时触发 |
焦点事件
焦点事件会在页面获得或者失去焦点的时候触发。利用这些事件和document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪。
| 方法/属性 | 说明 |
|---|---|
| blur | 在元素失去焦点时触发,不会冒泡 |
| DOMFocusIn | 在元素获得焦点时触发,与focus等价,但是会冒泡,只有 Opera 支持,在 DOM3 中被废弃 |
| DOMFocusOut | 在元素失去焦点时触发,是blur的通用版本,只有 Opera 支持,在 DOM3 中被废弃 |
| focus | 在元素获得焦点时触发,不会冒泡 |
| focusin | 在元素获得焦点时触发 |
| focusout | 在元素失去焦点时触发,会冒泡 |
当焦点从页面的一个元素移动到另一个元素,会依次触发下列事件:
focusout在失去焦点的元素上触发;focusin在获得焦点的元素上触发;blur在失去焦点的元素上触发;DOMFocusOut在失去焦点的元素上触发;focus在获得焦点的元素上触发;DOMFocusIn在获得焦点的元素上触发。
判断浏览器是否支持某些事件:
var isSupported = document.implementation.hasFeature("FoucusEvent", "3.0");
鼠标和滚轮事件
DOM3 中定义了以下 9 个鼠标事件,如下表:
| 事件 | 说明 |
|---|---|
| click | 单击或回车时触发 |
| dbclick | 双击鼠标触发 |
| mousedown | 鼠标按下时触发 |
| mouseenter | 鼠标首次进入元素范围触发,这事件不冒泡,且在其后代元素上不会触发 |
| mouseleave | 鼠标离开元素范围触发,这事件不冒泡,且在其后代元素上不会触发 |
| mousemove | 鼠标在元素内部移动时,就会不断触发 |
| mouseover | 鼠标位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发 |
| mouseout | 鼠标离开元素范围进入另一个元素范围时触发 |
| mouseup | 在用户释放鼠标按钮时触发 |
| mousewheel | 鼠标滚轮滚动时触发,Firefox 不支持 |
| DOMMouseScroll | 鼠标滚轮滚动时触发,Firefox 支持 |
坐标位置
| 属性 | 说明 |
|---|---|
| clientX | 相对于浏览器视口中的水平坐标 |
| clientY | 相对于浏览器视口中的垂直坐标 |
| pageX | 相对于页面的水平坐标(页面没有滚动时,等于 clientX),IE 下有 x 属性 |
| pageY | 相对于页面的垂直坐标(页面没有滚动时,等于 clientY),IE 下有 y 属性 |
| screenX | 相对于屏幕坐标的水平坐标 |
| screenY | 相对于屏幕坐标的垂直坐标 |
修改键
虽然鼠标事件主要是在鼠标上触发的,但在按下鼠标时键盘上的某些键状态也可以影响到所要采取的操作,修改键如下:
| 属性 | 说明 |
|---|---|
| shiftkey | 检测是否按下了 shift 键 |
| ctrlkey | 检测是否按下了 ctrl 键 |
| metakey | 检测是否按下了 meta 键 |
| altkey | 检测是否按下了 alt 键 |
当鼠标事件发生时,通过检测这几个属性就可以确定用户是否同时按下了其中的键。
相关元素
对mouseover事件而言,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素,mouseout则正好相反。
var eventUtil = {
// 省略其他代码
getRelatedTarget: function (event) {
if(event.relatedTarget) {
return event.relatedTarget;
} else if(event.toElement) {
// for ie mouseout event
return event.toElement;
} else if(event.fromElement) {
// for ie mouseover event
return event.fromElement;
} else {
return null;
}
}
// 省略其他代码
}
鼠标按钮
DOM 下button属性有三个值:0 表示主鼠标按钮,1 表示滚轮按钮,2 表示次鼠标按钮。 IE8 以下,button属性的值有 8 个:
- 0:没有按下按钮
- 1:按下主鼠标
- 2:按下次鼠标
- 3:表示同时按下了主、次鼠标按钮
- 4:表示按下了中间的鼠标按钮
- 5:表示同时按下了主鼠标按钮和中间鼠标按钮
- 6:表示同时按下了次鼠标按钮和中间鼠标按钮
- 7:表示同时按下了是哪个鼠标按钮
var eventUtil = {
// 省略已有代码
getButton: function(event) {
if(document.implementation.hasFeature("MouseEvents", "2.0")) {
return event.button;
} else {
// ie
switch (event.button) {
case 0:
case 1:
case 3:
case 5:
case 7:
return 0;
case 2:
case 6:
return 2;
case 4:
return 1;
}
}
}
}
更多事件信息
“DOM2 级事件”规范在event对象中提供了detail 属性,表示在给定位置上发生了多少次单击。
IE 也通过下列属性为鼠标事件提供了更多信息:
altLeft:表示是否按下了 Alt 键ctrlLeft:表示是否按下了 Ctrl 键offsetX:光标相对于目标元素边界的 x 坐标offsetY:光标相对于目标元素边界的 y 坐标shiftLeft:表示是否按下了 Shift 键。如果 shiftleft 的值为true,则 shiftKey 的值也为true
鼠标滚轮事件
IE下:mousewheel事件,wheelDelta属性表示鼠标滚动方向,是 120 的倍数。向上表示 +120,向下表示 -120。
FF下:DOMMouseScroll事件,detail属性表示鼠标滚动方向,是 3 的倍数。向上表示 -3,向下表示 +3。
var eventUtil = {
getWheelDelta: function(event) {
if(event.mousewheel){
return (client.engine.opera && client.engine.opera < 9.5) ? -wheelDelta : wheelDelta;
//在opera9.5之前,wheelDelta方向与现在相反,需要做一些处理
} else {
// for ff
return -event.detail*40;
}
}
}
键盘与文本事件
有三个键盘事件:
keydown:当用户按下键盘上的任意键时触发,如果按住不放,会重复触发事件。keypress:当用户按下键盘上的字符键时触发,如果按住不放,会重复触发事件。keyup:当用户释放键盘上的键触发。
键码和字符编码
在发生keydown和keyup事件时,event对象的keyCode属性会包含一个代码,与键盘上一个特定的键对应。发生keypress事件时,event对象会包含一个charCode属性,会包含按下键所代表字符的编码。IE8 以下不支持charCode属性,但是会在keyCode中保存字符的 ASCII 编码。
var eventUtil = {
getCharCode: function(event) {
if(typeof event.charCode == 'number') {
return event.charCode;
} else {
// IE8及以前,早期opera在keyCode中保存键码
return event.keyCode;
}
}
}
DOM3 级变化
DOM3 中不再包含charCode属性,而是包含两个新属性:key和char。key的值是一个字符串,按下某个字符键时,key的值就是相应的文本字符;按下非字符键,key的值是相应的键名(如“Shift”)。而char属性在按下字符键的行为与key相同,在按下非字符键时值为null。
textInput 事件
在 DOM3 级事件中引入,与keypress不同,只有在可编辑区域内输入字符的时候触发。出发该事件时,它的event对象中还包含一个data属性,这是属性就是用户输入的字符。event对象上还有一个属性,叫inputMethod,表示把文本输入到文本框中的方式。
- 0,表示浏览器不确定是怎么输入的
- 1,键盘输入
- 2,粘贴
- 3,拖放
- 4,IME 输入
- 5,在表单中选择某一项输入
- 6,手写输入
- 7,语音输入
- 8,几种方法组合输入
- 9,通过脚本输入
复合事件
用于处理 IME(Input Method Editor,输入法编辑器)的输入序列,可以让用户输入在物理键盘上找不到的字符。
compositionstart:在 IME 的文本复合系统打开时触发,表示要开始输入。compositionupdate:在向输入字段中插入新字符时触发。compositionend:在 IME 的文本复合系统关闭时触发,表示返回正常键盘输入状态。
触发该事件时,目标是接收文本的输入字段,比文本事件的时间对象多一个属性data,其中包含以下几个值中的一个:
- 如果
compositionstart事件发生时访问,包含正在编辑的文本; - 如果
compositionupdate事件发生时访问,包含正插入的新字符; - 如果
compositionend事件发生时访问,包含此次输入会话中插入的所有字符。
变动事件
DOM2 级的变动事件能在 DOM 中某一部分发生改变时给出提示。
| 事件 | 说明 |
|---|---|
| DOMSubtreeModified | 在 DOM 结构中发生任何变化时触发 |
| DOMNodeInserted | 一个节点作为子节点被插入到另一个节点中时触发 |
| DOMNodeRemoved | 节点被移除的时候触发 |
| DOMNodeInsertedIntoDocument | 节点被直接插入文档,或通过子树间接插入文档的时候触发。在DOMNodeInserted之后触发 |
| DOMNodeRemovedFromDocument | 节点被直接从文档中移除,或通过子树被移除的时候触发,在DOMNodeRemoved之后被触发 |
| DOMAttrModified | 在特性被修改之后触发 |
| DOMCharacterDataModified | 在文本节点的值发生变化的时候触发 |
event.relatedNode保存着相关节点。可以类比relatedTarget。
HTML5事件
contextmenu 事件
用以表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义菜单。
beforeunload 事件
为了让开发人员有可能在页面卸载前阻止这一操作,这个事件会在浏览器卸载页面之前触发。但是不能彻底取消这个事件,因为那就相当于让用户无法离开当前页面。这个事件的意图是将控制权交给用户。为了显示这个弹出对话框,必须将event.returnValue的值设置为要显示给用户的字符串,同时作为函数的值返回。
DOMContentLoaded 事件
window的load事件会在页面中的一切都加载完毕时触发,但这个过程可能会因为要加载的外部资源过多而颇费周折。而DOMContentLoaded事件则在形成完整的 DOM 树之后就会触发,不理会图像、JavaScript 文件、CSS 文件或其他资源是否已经下载完毕。
readystatechange 事件
IE 为 DOM 文档中的某些部分提供了readystatechange事件。这个事件的目的是提供与文档或元素的加载状态有关的信息,支持该时间的每个对象都有一个readyState属性,可能包含下列 5 个值中的一个。
- uninitialized(未初始化):对象存在但尚未初始化。
- loading(正在加载):对象正在加载数据。
- loaded(加载完毕):对象加载数据完成。
- interactive(交互):可以操作对象了,但还没有完全加载。
- complete(完成):对象已经加载完毕。
pageshow 和 pagehide 事件
Firefox 和 Opera 有一个特性,名叫“往返缓存(back--forward cache,或 bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。
pageshow在页面显示时触发,无论是否来自 bfcache。在重新加载的页面中,pageshow会在load事件触发后触发;而对于 bfcache 中的页面,pageshow会在页面状态完全恢复的那一刻触发。pageshow事件的event对象还包含一个名为persisted的布尔值属性。如果页面被保存在 bfcache 中,则为true;否则,为false。pagehide事件触发时根据persisted的值采取不同的操作。
hashchange 事件
便于在 URL 的参数列表发生变化时通知开发人员。之所以新增这个事件,是因为在 Ajax 应用中,开发人员经常要利用 URL 参数列表来保存状态或导航信息。
必须要把该事件处理程序添加给window对象,然后 URL 参数列表只要变化就会调用它。此时的event对象应该额外包含两个属性:oldURL和newURL。这两个属性分别保存着参数列表前后的完整 URL。
设备事件
orientationchange 事件
移动 Safari 中的事件,window.orientation属性可能包含 3 个值:0 表示肖像模式,90 表示向左旋转的横向模式,-90 表示向右旋转的横向模式。
MozOrientation 事件
与 iOS 中的事件不同,该事件只能提供一个平面的方向变化。此时event对象包含三个属性:x,y 和 z。这几个属性都介于 1 到 -1 之间,表示不同坐标轴上的方向。在静止状态下,x 值为 0,y 值为 0,z 值为 1。如果设备右倾,x 值会减小,向左倾,x 值会增大。如果设备向远离用户的方向倾斜,y 值会减小,向接近用户的方向倾斜 y 值会增大。z 轴检测垂直加速度,1 表示静止不动,在设备移动时值会减小。失去重量状态下值为 0。
deviceorientation 事件
触发该事件时,事件对象包含着每个轴相对于设备静止状态下发生变化的信息。事件对象包含以下 5 个属性:
alpha:在围绕 z 轴旋转时(左右旋转),y 轴的度数差;是一个介于 0 到 360 之间的浮点数。beta:在围绕 x 轴旋转时(前后旋转),z 轴的度数差;是一个介于 -180 到 180 之间的浮点数。gamma:在围绕 y 轴旋转时(扭转设备),z 轴的度数差;是一个介于 -90 到 90 之间的浮点数。absolute:布尔值,表示设备是否返回一个绝对值。compassCalibrated:布尔值,表示设备的指南针是否校准过。
devicemotion 事件
触发 devicemotion 事件时,事件对象包含以下属性:
acceleration:一个包含 x、y 和 z 属性的对象,在不考虑重力的情况下,告诉你在每个方向上的加速度。accelerationIncludingGravity:一个包含 x、y 和 z 属性对象,在考虑 z 轴自然重力加速度的情况下,考诉你在每个方向上的加速度。interval:以毫秒表示的时间值,必须在另一个 devicemotion 事件触发前传入。这个值在每个时间中应该是一个常量。rotationRate:一个包含表示方向的alpha、beta和gamma属性的对象。
触摸与手势事件
触摸事件
触摸事件会在用户手指放在屏幕上面时、在屏幕上滑动时或从屏幕上移开时触发。具体来说,有以下几个触摸事件:
touchstart:当手指触摸屏幕时触发,即使有一个手指放在屏幕上也会触发。toucehmove:当手指在屏幕上滑动时连续地触发。touchend:当手指从屏幕移开时触发。touchcancel:当系统停止跟踪触摸时触发。
除了常见的 DOM 属性外,触摸事件还包含下列三个用于跟踪触摸的属性:
touches:表示当前跟踪的触摸操作的 Touch 对象的数组。targetTouches:特定于事件目标的 Touch 对象的数组。changeTouches:表示自上次触摸依赖发生了什么改变的 Touch 对象的数组。
每个 Touch 对象包含下列属性:
clientX:触摸目标在视口中的 x 坐标。clientY:触摸目标在视口中的 y 坐标。pageX:触摸目标在页面中的 x 坐标。pageY:触摸目标在页面中的 y 坐标。screenX:触摸目标在屏幕中的 x 坐标。screenY:触摸目标在屏幕中的 y 坐标。identifier:标识触摸的唯一 ID。target:触摸 DOM 节点目标。
这些事件会在文档的所有元素上面触发,因而可以分别操作页面的不同部分。在触摸屏幕上的元素时,这些事件发生的顺序如下:
touchstartmouseovermousemovemousedownmouseupclicktouchend
手势事件
两个手指触摸屏幕时就会产生手势,手势通常会改变显示项的大小,或者旋转显示项。有三个手势事件:
gesturestart:当一个手指已经按在屏幕上而另一手指又触摸屏幕时触发。gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。gestureend:当任何一个手指从屏幕上移开时触发。
该事件包含两个额外属性
rotation:手指变化引起的旋转角度,负值表示逆时针旋转,正值表示顺时针旋转。scale:表示两个手指间距离的变化情况;这个值从 1 开始,并随距离拉大而增长,随距离缩短而减小。
内存和性能
事件委托
只需在 DOM 树中尽量最高的层次上添加一个事件处理程序:
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
var list = document.getElementById('myLinks');
EventUtil.addHandler(list, 'click', function(event) {
var target = EventUtil.getTarget(event);
target.style.backgroundColor = 'red';
});
移除事件处理程序
在 Web 应用程序中,过时不用的事件处理程序是影响性能的一个重要原因。
当直接移除带有事件处理函数的元素的时候,造成事件处理函数保持引用,无法被回收。 在进行元素移除的时候,首先对事件处理程序进行手工移除。
当进行页面卸载的时候,如果在事件处理程序没有被清理干净之前卸载完成,事件处理程序就会滞留在内存中,每次加载完在卸载页面的时候,滞留的对象就会增加。 应该在卸载前通过 onunload 事件移除事件处理程序。
模拟事件
DOM中的事件模拟
通过document.createEvent()创建event对象。在DOM2级中,所有事件使用英文复数,DOM3级中,使用单数。
- UIEvents:一般化的 UI 事件。鼠标事件和键盘事件都继承自 UI 事件。DOM3 级中是 UIEvent。
- MouseEvents:一般化的鼠标事件。DOM3 级中是 MouseEvent。
- MutationEvents:一般化的 DOM 变动事件。DOM3 级中是 MutationEvent。
- HTMLEvents:一般化的 HTML 事件。没有对应的 DOM3 级事件。
模拟事件的最后一步就是触发事件,这一步需要使用dispatchEvent(),调用该方法要传入一个参数,即表示要触发的事件的event对象。
模拟鼠标事件
模拟鼠标事件的做法是向createEvent()传入字符串"MouseEvent"。返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息。该方法接受 15 个参数:type、bubbles、cancelable、view(基本设置为document.defaultView)、detail、screenX、screenY、clientX、clientY、ctrlKey、altKey、shiftKey、metaKey、button、relatedTarget。
var btn = document.getElementById('myBtn');
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
btn.dispatchEvent(event);
模拟键盘事件
与鼠标事件类似,传入"KeyboardEvent"就可以创建一个键盘事件,返回对象包含initKeyEvent()方法。该方法接受如下参数:type、bubbles、cancelable、view、key、location、modifiers、repeat。Firefox 中传入"KeyEvents"可以创建一个键盘事件,参数如下:type、bubbles、cancelable、view、ctrlKey、altKey、shiftKey、metaKey、keyCode、charCode。
var textbox = document.getElementById('myTextbox');
if(document.implementation.hasFeature('KeyboardEvents', '3.0')) {
var event = document.createEvent('KeyboardEvents');
event.initKeyboardEvent('keydown', true, true, document.defaultView, 'a', 0, 'Shift', 0);
textbox.dispatchEvent(event);
}
// 模拟按下A同时按下shift键。
以上代码虽然能模拟键盘事件的触发,却无法向文本域输入任何字符,这是由于无法精确模拟键盘事件造成的。
模拟其他事件
除了鼠标和键盘事件,有时同样需要模拟变动事件和 HTML 事件。要模拟变动事件,可以使用createEvent('MutationEvents'),该方法接受参数包括:type、bubbles、cancelable、relatedNode、preValue、newValue、attrName、attrChange。
var event = document.createEvent('MutationEvents');
event.initMutaionEvent('DOMNodeInserted', true, false, someNode, '', '', '', 0);
target.dispatchEvent(event);
模拟 HTML 事件也一样,通过createEvent('HTMLEvents'),然后再使用这个对象的initEvent()方法来初始化它。
var event = document.createEvent('HTMLEvents');
event.initMutaionEvent('focus', true, false);
target.dispatchEvent(event);
自定义 DOM 事件
DOM3 级定义了“自定义事件”。自定义事件不是由原生 DOM 触发的,它的目的是让开发人员创建自己的事件。同样,要调用createEvent('CustomEvent'),返回的对象有一个名为initCustomEvent()的方法,接受如下 4 个参数:type、bubbles,cancelable,detail。
IE 中的事件模拟
思路类似,不过创建event对象使用的是document.createEventObject(),与 DOM 方式不同,这方法不接受参数,结果会返回一个通用的event对象。然后,必须手工添加必要信息。最后一步就是在目标上调用fireEvent()方法。
var event = document.createEventObject(); // 不接收参数
// 初始化事件对象,必须手工添加属性
event.altKey = false;
event.ctrlKey = false;
event.keyCode = 65;
// 对添加的属性没有任何限制,即使添加的属性不支持也不所谓。
// 触发事件
textbox.fireEvent('onkeypress', event)