1. 事件流
1.1 javascript事件
javascript与HTML之间的交互是通过事件实现的。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器来处理事件,以便事件发生时执行相应的代码。
事件流描述的是从页面中接收事件的顺序,IE的事件流是冒泡流,而Netscape Communicator的事件流是事件捕获流。
1.2 事件冒泡
IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点,例如:
<html> <head> <title>Event Bubbling Example</title> </head> <body> <div id="mydiv">Click</div> </body> </html>
如果单击了页面中的<div>元素,那么这个click事件会按照<div>、<body>、<html>、document的顺序传播。所有现代浏览器都支持事件冒泡。
1.3 事件捕获
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到。如1.2中的例子,click事件的触发顺序是document、<html>、<body>、<div>。除Netscape Communicator外,Safari、Chrome、Opera和Firefox也都支持事件流模型。尽管DOM2规范要求事件应从document对象开始传播,但这四种浏览器都是从window对象开始捕获事件的。
1.4 DOM事件流
DOM2规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。在DOM事件流中,实际的目标(div)在捕获阶段不会接收到事件,接着事件在div上发生,并在事件处理中被看成冒泡阶段的一部分。但实际上Safari、Chrome、Firefox和Opera9.5级更高版本都会在捕获阳段触发事件对象上的事件。
2. 事件处理程序
2.1 HTML事件处理程序
事件就是用户或浏览器自身执行的某种动作,如click、load和mouseover,都是事件的名字,而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序以on开头,如onclick。
某个元素支持的每种事件,都可以使用一个相应事件处理程序同名的HTML特性指定,例如:
<input type="button" value="Click" onclick="alert('Clicked')"/>
也可以调用在页面其他地方定义的脚本,例如:
<input type="button" value="Click" type="text/javascript"> function show() { alert("Clicked"); } </script>
2.2 DOM0级事件处理程序
通过Javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性,例如:
var mydiv = document.getElementById("mydiv"); mydiv.onclick = function() { alert("Clicked"); }
使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此这时候的事件处理程序是在元素的作用域中运行,例如:
var mydiv = document.getElementById("mydiv"); mydiv.onclick = function() { alert(this.id); }
也可以删除通过DOM0级方法指定的事件处理程序,例如:
var mydiv = document.getElementById("mydiv"); mydiv.onclick = null;
2.3 DOM2级事件处理程序
DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有DOM节点都包含这两个方法,并且接受3个参数:要处理的事件名,作为事件处理程序的函数和一个布尔值,布尔值参数如果是true,表示在捕获阶段调用事件处理程序,如果是false,表示在冒泡阶段调用事件处理程序,例如:
var mydiv = document.getElementById("mydiv"); mydiv.addEventListener("click", function() { alert(this.id); }, false);
可以添加多个事件处理程序,会按照添加它们的顺序触发。通过addEventListener()添加removeEventListener()来移除,这也意味着通过addEventListener()添加的匿名函数将无法移除,例如:
var mydiv = document.getElementById("mydiv"); mydiv.removeEventListener("click", function() { alert(this.id); }, false); //无效
可以重写为:
var mydiv = document.getElementById("mydiv"); var handler = function() { alert(this.id); }; mydiv.addEventListener("click", handler, false); mydiv.removeEventListener("click", handler, false); //有效
2.4 IE事件处理程序
IE实现了与DOM中类拟的两个方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数,IE只支持事件冒泡,例如:
var mydiv = document.getElementById("mydiv"); mydiv.attachEvent(" function() { alert(this.id); });
在IE中使用attachEvent()事件处理程序会在全局作用域中运行,因此this等于window。类似的,attachEvent(),方法也可以用来为一个元素添加多个事件处理程序,只是以相反的顺序被触发。
使用detachEvent()可以移除attachEvent()添加的事件处理程序,条件是提供相同的参数。同样的,匿名函数将不能被移除,可以使用如下代码:
var mydiv = document.getElementById("mydiv"); var handler = function() { alert(this.id); }; mydiv.attachEvent("click", handler); mydiv.detachEvent("click", handler);
3. 事件对象
3.1 UI事件
UI事件主要与元素的焦点相关,而且仅在兼容DOM的浏览器中受到了支持。有3个UI事件为:
1) DOMActive: 表示元素已经被用户操作激活。
2) DOMFocusIn: 表示元素已经获得了焦点,这是HTML中focus事件的普通版。
3) DOMFocusOut: 表示元素已经失去了焦点,这是HTML中blur事件的普通版。
支持这几个UI事件的浏览器已经很少,因此不推荐使用。
3.2 鼠标事件
鼠标事件是WEB上面最常用的一类事件,DOM中定义了7个鼠标事件:
1) click: 在用户单击主鼠标按钮或者按下回车键时触发。
2) dblclick: 在用户双击主鼠标按钮时触发。
3) mousedown: 在用户按下任意鼠标按钮时触发。
4) mouseout: 在鼠标指针位于一个元素上方,而后用户将其移入另一个元素时触发。
5) mouseover: 在鼠标指针位于一个元素外部,而后用户将其移入另一个元素内时触发。
6) mouseup: 在用户释放鼠标按钮时触发。
7) mousemove: 当鼠标指针在元素内部移时重复地触发。
页面上所有元素都支持鼠标事件,所有鼠标事件都会冒泡,也可以被取消,而取消鼠标事件将会影响浏览器的默认行为。
虽然鼠标事件主要使用鼠标来触发,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键就是Shift、Ctrl、Alt和Meta,它们经常被用来修改鼠标事件的行为。DOM为此规定了4个属性,表示这些修改键的状态:shiftKey、ctrlKey、altKey和metaKey。如果相应的键被按下了,则值为true,否则值为false。
只有在主鼠标按钮被单击时才会触发click事件,但是对于mousedown和mouseup事件,其event对象存在一个button属性,表示按下或释放的按钮:0表示主鼠标按钮,1表示中间滚轮按钮,2表示次鼠标按钮。
IE中的button属性与DOM的button属性有很大差异:
1) 0: 表示没有按下按钮。
2) 1: 表示按下了主鼠标按钮。
3) 2: 表示按下了次鼠标按钮。
4) 3: 表示同时按下了主、次鼠标按钮。
5) 4: 表示按下了中间鼠标按钮。
6) 5: 表示同时按下了主鼠标按钮和中间的鼠标按钮。
7) 6: 表示同时按下了次鼠标按钮和中间的鼠标按钮。
8) 7: 表示同时按下了三个鼠标按钮。
支持DOM版鼠标事件的浏览器可通过hasFearture()方法来检测。
3.3 键盘事件
有3个键盘事件如下:
1) keydown: 当用户按下键盘上的任意键时触发,如果按住不放会重复触发此事件。
2) keypress: 当用户按下键盘上的字符键时触发,如果按住不放会重复触发此事件。
3) keyup: 当用户释放键盘上的键时触发。
在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上的特定的键对应,对数字字母字符键,keyCode属性与ASCII码中对应小写字母或数字的编码相同。
以下是keydown或keyup事件存在的特殊情况:
1) 在Firefox和Opera中,按分号键时keyCode值为59,但IE和Safari返回186。
2) 在Safari 3之前的版本中,上、下、左、右箭头和上、下翻页键返回大于63000的值。
3) 在Opera 9.5之前的版本中,会将非数字字母键的keyCode设置为等于相应字符的ASCII编码。
4) 在Safari 3之前的版本中,不会因为按下了制表、上档、控制或替代键而触发keydown和keyup事件。
Firefox、Chrome和Safari的event对象都支持一个charCode属性,这个属性只有在发生keypress事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII编码。IE和Opera则是在keyCode中保存字符的ASCII编码。
3.4 HTML事件
HTML事件包括下列几个事件:
1) load: 当页面完全加载后window上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在<imt>元素上触发,当嵌入的内容加载完毕时在<object>元素上面触发。
2) unload: 当页面完全卸载后在window上面触发。
3) abort: 在用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上面触发。
4) error: 当发生JavaScript错误时在window上面触发,当有一或多个框架无法加载时在框架集上面触发,当无法加载图像时在<img>元素上面触发,当无法加载嵌入内容时在<object>元素上面触发。
5) select: 当用户选择文本框中的一或多个字符时触发。
6) change: 当文本框失去焦点或者在取得焦点后其值被改变时触发。
7) submit: 当用户单击提交按钮时在<form>元素上面触发。
8) reset: 当用户单击重置按钮时在<form>元素上面触发。
9) resize: 当窗口或框架的大小变化时在window或框架上面触发。
10) scroll: 当用户滚动带滚动条的元素中的内容时,在该元素上面触发。
11) focus: 当页面或元素获得焦点时在window及相应元素上面触发。
12) blur: 当页面或元素失去焦点时在window及相应元素上面触发。
3.5 变动事件
DOM2级的变动事件能在DOM中的某一部分发生变化时给出提示,DOM2级定义了如下变动事件:
1) DOMSubtreeMdofied: 在DOM结构中发生任何变化时触发。
2) DOMNodeInserted: 在一个节点作为子节点被插入到另一个节点中时触发。
3) DOMRemoved: 在节点从其父节点中被移除时触发。
4) DOMNodeInsertedIntoDocument: 在一个节点被直接插入文档或通过子树间接插入文档之后触发。
5) DOMNodeRemovedFromDocument: 在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。
6) DOMAttrModified: 在特性被修改之后触发。
7) DOMCharacterDataModified: 在文本节点的值发生变化时触发。
不是所有浏览器都支持所有事件,可以使用代码检测是否支持,例如:
var isSupported = document.implementation.hasFeature("MutationEvents", "2.0");
事件 | Opera 9 | Firefox 2 | Firefox 3 | Safari及Chrome |
DOMSubtreeModified | - | - | 支持 | 支持 |
DOMNodeInserted | 支持 | 支持 | 支持 | 支持 |
DOMNodeRemoved | 支持 | 支持 | 支持 | 支持 |
DOMNodeInsertedIntoDocument | 支持 | - | - | 支持 |
DOMNodeRemovedFromDocument | 支持 | - | - | 支持 |
DOMAttrModified | 支持 | 支持 | 支持 | - |
DOMCharacterDataModified | 支持 | 支持 | 支持 | 支持 |
4. 专有事件
4.1 上下文菜单事件
为了实现上下文菜单,出现了contextmenu事件,用以表示何时应该显示上下文菜单。由于contextmenu事件是冒泡的,因此可以为document指定一个事件处理程序,用以处理页面中发生的所有此类事件。这个事件的目标是发生用户操作的元素。所有浏览器中都可以取消这个事件:在兼容DOM的浏览器中,使用event.preventDefault(),在IE中,将event.returnValue值设置为false,例如:
EventUtil.addHandler(window, "load", function(event) { var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "contextmenu", function(event) { event = EventUtil.getEvent(event); EventUtil.preventDefault(event); var menu = document.getElementById("myMenu"); menu.style.left = event.clientX + "px"; menu.style.top = event.clientY + "px"; menu.style.visibility = "visible"; }); EventUtil.addHandler(document, "click", function(event) { document.getElementById("myMenu").style.visibility = "hidden"; }); });
这个事件处理程序首先会取消默认行为,以保证不显示浏览器默认的上下文菜单,再根据event对象的clientX和clientY属性的值,来确定放置菜单的位置,最后通过visibility属性设置显示自定义上下文菜单。
4.2 卸载前事件
为了让开发人员有可能在页面卸载前阻止这一操作,所以有beforeunload事件。这个事件会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面,但是不能彻底取消这个事件。将event.returnValue的值设置为要显示给用户的字符串,例如:
EventUtil.addHandler(window, "beforeunload", function(event) { event = EventUtil.getEvent(event); event.returnValue = "Some message."; });
4.3 鼠标滚轮事件
IE6.0首先实现了mousewheel事件,此后Opera、Chrome和Safari也都实现了这个事件。当用户通过鼠标滚轮与页面交互,就会触发mousewheel事件。这个事件可以在任何元素上触发,最终会冒泡到document(IE)或window(Opera、Chrome及Safari)对象。mousewheel事件还包含一个wheelDelta属性,当用户向前滚动时,wheelDelta是120的倍数,当用户向后滚动时,wheelDelta是-120的倍数,例如:
EventUtil.addHandler(document, "mousewheel", function(event) { event = EventUtil.getEvent(event); alert(event.wheelDelta); });
4.4 DOM载入事件
DOMContentLoaded事件在形成完整的DOM树之后就会触发,不理会图像、JavaScript文件、CSS文件或其他资源是否已经下载完毕。要处理DOMContentLoaded事件,可以为document或window添加相应的事件处理程序,尽管这个事件会冒泡到window,但它的目标实际上是document,例如:
EventUtil.addHandler(document, "DOMContentLoaded", function(event) { alert("Content loaded."); });
4.5 就绪状态变化事件
IE为DOM文档中的某些部分提供了readystatechange事件,这个事件的目的是提供与文档或元素的加载状态有关的信息,支持readstatechange事件的每个对象都有一个readyState属性,可以包含下列5个值中的一个:
1) uninitialized: 对象存在但尚未初始化。
2) loading: 对象正在加载数据。
3) loaded: 对象加载数据完成。
4) interactive: 可以操作对象了,但还没有完全加载。
5) complete: 对象已经加载完成了。
例如:
EventUtil.addHandle(document, "readystatechange", function(event) { if (document.readyState == "interactive") { alert("Content loaded."); } });
4.6 页面显示和页面隐藏
Friefox和Opera有一个特性,叫“往返缓存”,可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。如果页面位于缓存中,那么再次打开该页面时不会触发load事件。为了更形象说明缓存行为,Firefox提供了一些新事件,第一个事件是pageshow,这个事件在页面显示时触发,无论是否有缓存,与pageshow对应的是pagehide事件,该事件会在浏览器卸载页面的时候触发,例如:
EventUtil.addHandler(window, "pageshow", function(event) { showcount++; alert("show has been fired!"); });
5. 内存和性能
5.1 事件委托
添加到页面上的事件处理程序数量直接关系页面的整体运行性能。对事件过多的解决可以使用事件委托。事件委托利用了冒泡事件,只指定一个事件处理程序,就可以管理某个类型的所有事件,例如:
var list = document.getElementById("myLinks"); EventUtil.addHandler(list, "click", function(event) { event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id) { case"something": alert("something"); break; ... } });
5.2 移除事件处理程序
每当将事件处理程序指定给元素时,运行中浏览器代码与支持页面交互的JavaScript之间就会建立一个连接。这种连接越多,页面执行起来就越慢。内存中留有不用的空事件处理程序,也是造成WEB应用内存与性能问题的主要原因。
有两种情况可能造成上述问题:第一种是从文档中移除带有事件处理程序的元素,原来添加到元素中的事件处理程序有可能无法当作垃圾回收。另一种是卸载页面的时候,如果没有清理干净事件处理程序,那它们就会滞留在内存中。
因此不再使用的事件处理程序最好手工删除,例如:
var div = document.getElementById("mydiv"); div.onclick = function() { div.onclick = null; ...... }
6. 模拟事件
6.1 DOM中的事件模拟
可以在document对象上使用createEvent()方法创建event对象。这个方法接受一个参数,即要创建的事件类型的字符串:
1) UIEvents: 一般化的UI事件。
2) MouseEvents: 一般化的鼠标事件。
3) MutationEvents: 一般化的DOM变动事件。
4) HTMLEvents: 一般化的HTML事件。
在创建了event对象之后,还需要使用与事件有关的信息对其进行初始化。每种类型的event对象都有一个特殊的方法,为它传入适当的数据就可以初始化该event对象。
模拟事件的最后一步是触发事件。这一步需要使用dispatchEvent()方法,所有支持事件的DOM节点都支持这个方法。调用dispatchEvent()方法需要传入一个参数,即表示要触发事件的event事件。
6.2 模拟鼠标事件
创建鼠标事件需要为createEvent()传入字符串"MouseEvents",返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息,这个方法接受15个参数,分别与鼠标事件中的每个典型属性一一对应:
1) type: 表示要触发的事件类型,如"click"。
2) bubbles: 表示事件是否应该冒泡。
3) cancelable: 表示事件是否可以取消。
4) view: 与事件关联的视图,几乎总是"document.defaultView"。
5) detail: 与事件有关的详细信息,通常设置为0。
6) screenX: 事件相对于屏幕的X坐标。
7) screenY: 事件相对于屏幕的Y坐标。
8) clientX: 事件相对于视口的X坐标。
9) clientY: 事件相对于视口的Y坐标。
10) ctrlKey: 表示是否按下了Ctrl键,默认为false。
11) altKey: 表示是否按下了Alt键,默认为false。
12) shiftKey: 表示是否按下了Shift键,默认为false。
13) metaKey: 表示是否按下了Meta键,默认为false。
14) button: 表示按下了哪一个鼠标键,默认值为0.
15) relatedTarget: 表示与事件相关的对象,只在mouseover或mouseout时使用。
6.3 模拟键盘事件
DOM2级事件中没有就键盘事件作出规定,Firefox根据DOM2级原有草案实现了键盘事件。在Firefox中,调用createEvent()并传入"KeyEvents"就可以创建一个键盘事件。返回的事件会包含一个initKeyEvent()方法,这个方法接受10个参数:
1) type: 表示要触发的事件类型。
2) bubbles: 表示事件是否应该冒泡。
3) cancelable: 表示事件是否可以取消。
4) view: 与事件关联的视图。
5) ctrlKey: 表示是否按下了Ctrl键,默认为false。
6) altKey: 表示是否按下了Alt键,默认为false。
7) shiftKey: 表示是否按下了Shift键,默认为false。
8) metaKey: 表示是否按下了Meta键,默认为false。
9) keyCode: 被按下或释放的键的键码,只在keydown或keyup时使用,默认为0。
10) charCode: 通过按键生成的字符的ASCII编码,只在keypress时使用,默认为0。
6.4 模拟其他事件
要模拟变动事件,可以使用createEvent("MutationEvents")创建一个包含initMutationEvent()方法的变动事件对象。这个方法接受的参数包括:type、bubbles、cancelable、relatedNode、preValue、newValue、attrName和attrChange。
6.5 IE中的事件模拟
在IE中模拟事件与在DOM中模拟事件的思路相似:先创建event对象,然后为其指定相应的信息,然后再使用该对象来触发事件。
调用document.createEventObject()方法可在IE中创建event对象。但与DOM方式不同的是,这个方法不接受参数,结果会返回一个通用的event对象,然后再手工为这个对象添加所有必要的信息。最后在目标上调用fireEvent(),这个方法接受两个参数:事件处理程序的名称和event对象。在调用fireEvent()方法时,会自动为event对象添加srcElement和type属性,其他属性都必须通过手工添加。
本文出自 “青锋笔记” 博客,谢绝转载!