事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。
一、事件流
事件流描述的是从页面接收事件的顺序。
IE和Netscape开发团队的具有几乎完全相反的事件流概念。
IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。
1.事件冒泡
事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
2.事件捕获
事件捕获(event capturing),事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。
3.DOM事件流
“DOM2级事件”规定事件流包括:事件捕获阶段、处于目标阶段和时间冒泡阶段。
首先发生的是事件捕获阶段,为截获事件提供机会。染回是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
二、事件处理程序
事件就是用户或浏览器自身执行的某种动作。诸如click
、load
、mouseover
,都是事件的名字。
而响应某个事件的函数就叫作事件处理程序(事件侦听器)。事件处理程序的名字都是以on
开头的,如:onclick
、onmouseover
、onload
…
1.HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。
<input type="button" value="Click me" onclick="alert('Clicked')"/>
<input type="button" value="Click me" onclick="alert("clicked")" />
<input type="button" value="Click me" onclick="showMessage()"/>
<script type="text/javascript">
function showMessage(){
alert("Hello World!");
}
</script>
需要注意的是:如果HTML特性值中时JavaScript代码,则不能在其中使用未经转义的HTML语法字符。
为了避免使用HTML实体,这里使用了单引号。
通过event
变量,可以直接访问事件对象。且在函数内部,this
值等于事件的目标元素。
且在事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。
HTML事件处理程序的缺点:
- 时差问题;
- 扩展事件处理程序的作用域链在不同浏览器中会导致不同的结果;
- HTML与JavaScript代码紧密耦合;
2.DOM0级事件处理程序
将一个函数赋值给一个事件处理程序属性。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(this.id); //"myBtn"
}
3.DOM2级事件处理程序
用于处理指定和删除事件处理程序的操作:
addEventListener()
和removeEventListener()
- 所有DOM节点都包含这俩个参数方法。
- 它们都接受三个参数:要处理的事件名、作为事件处理程序的函数、布尔值;
- 布尔值参数为
true
,表示在捕获阶段调用事件处理程序;为false
,表示在冒泡阶段调用事件处理程序。 - 可以添加多个事件处理程序。
var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
btn.addEventListener("click",function(){
alert("Hello World!");
},false);
移除事件处理程序:
- 传入
removeEventListener()
中的参数必须与addEventListener()
中的一致。
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click",handler,false);
btn.removeEventListener("click",handler,false);
大多数情况下,都是将时间处理程序添加到事件流的冒泡阶段,这样可以最大限度的兼容各种浏览器。
4.IE事件处理程序
IE中实现了与DOM中类似的俩个方法:
attachEvent()
和detachEvent()
- 这俩个方法都接受相同的俩个参数:事件处理程序名称、事件处理程序函数;
- 添加的事件处理程序会被添加到冒泡阶段;
- 可以添加多个事件,以添加的相反顺序触发;
①
attachEvent()
的第一个参数是onclick
;而非DOM的addEventListener()
方法中的click
;
②使用DOM0级方法的情况下,时间处理程序会在其所属元素的作用域内运行;在使用attachEvent()
方法的情况下,时间处理程序会在全局作用域中运行,因此this
等于window
。
5.跨浏览器的时间处理程序
为了以跨浏览器的方式处理事件,开发人员会使用能够隔离浏览器差异的JavaScript库,还有一些开发人员会开发最合适的事件处理方法。
三、事件对象
在触发DOM上的某个事件时,会产生一个事件对象event
,这个对象中包含着所有与事件有关的信息。
1.DOM中的事件对象
兼容DOM的浏览器会将一个event
对象传入到事件处理程序中。
event
对象包含与创建它的特定事件有关的属性和方法。
- 在事件处理程序内部,对象
this
始终等于currentTarget
属性(时间处理程序当前正在处理事件的那个元素)的值;而target
属性(事件的目标)则只包含事件的实际目标。 - 需要通过一个函数处理多个事件时,可以使用
type
属性(被触发的事件类型); - 要阻止特定事件的默认行为,可以使用
preventDefault()
方法; - 只有
cancelable
属性(是否可以取消事件的默认行为)设置为true
的事件,才可以使用prevnetDefault()
方法。 stopPropagation()
方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。- 事件对象的
eventPhase
属性可以确定事件当前正处于事件流的哪个阶段:1
表示捕获阶段;2
表示"处于目标";3
表示冒泡阶段;
//使用type属性处理多个事件
var btn = document.getElementById("myBtn");
var handler = function(event){
switch(event.type){
case "click":
alert("clicked");
break;
case "mouseover":
event.target.style.backgroundColor = "red";
break;
case "mouseover":
event.target.style.backgroundColor = "";
break;
}
};
btn.onclick = handler;
btn.onmouserover = handler;
btn.onmouseout = handler;
//使用preventDefault()方法阻止特定事件的默认行为
var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
};
//使用stopPropagation()方法取消进一步的事件捕获或冒泡
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("Clicked");
event.stopPropagation();
};
document.body.onclick = function(event){
alert("Body clicked");
};
2.IE中的事件对象
与访问DOM中的event
对象不同,要访问IE中的event
对象有几种不同的方式,取决于指定时间处理程序的方法。
在使用DOM0级方法添加事件处理程序时,event
对象作为window
对象的一个属性存在。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
var event = window.event;
alert(event.type); //"click"
}
IE中的事件对象也会包含一些属性和方法:
cancelBubble
:默认为false
,但将其设置为true
就可以取消事件冒泡;returnValue
:默认值为true
,但将其设置为false
就可以取消事件的默认行为;srcElement
:事件的目标type
:被触发的事件的类型
因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为
this
会始终等于事件目标。
3.跨浏览器的事件对象
四、事件类型
1.UI事件
DOMActivate
、load
、unload
、error
、abort
、select
、resize
、scroll
load事件
当页面完全加载后(包括图片、JavaScript文件、CSS文件等外部资源),就会触发window
上面的load
事件。
unload事件
当文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,就会发生unload
事件。而利用这个事件最多的情况是清除引用,以避免内存泄漏。
resize事件
当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize
事件。
浏览器最小化或最大化也会触发resizi
事件。
scroll事件
2.焦点事件
blur
、DOMFocusIn
、DOMFocusOut
、focus
、focusin
、focusout
3.鼠标与滚轮事件
click
、dbclick
、mousedown
、mouseenter
、mouseleave
、mousemove
、mouseout
、mouseover
、mouseup
、mousewheel
4.键盘与文本事件
keydown
、keypress
、keyup
、textInput
5.复合事件
compositionstart
、compositionupdate
、compositionend
6.变动事件
DOMSubtreeModified
、DOMNodeInserted
、DOMNodeRemoved
、DOMNodeInsertIntoDocument
、DOMNodeRemoveFromDocument
、DOMAttrModified
、DOMCharacterDataModified
7.HTML5事件
①contextmenu事件
用以表示何时应该显示上下文菜单,该事件是冒泡的。
通常使用contextmenu
事件来显示自定义的上下文菜单,而使用onclick
事件处理程序来隐藏该菜单。
②beforeunload事件
这个事件会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面。
③DOMContentLoaded事件
DOMContentLoaded
事件在形成完整的DOM树之后会触发,不理会图像、JavaScript文件、CSS文件或其他资源是否已经下载完毕。
与load
事件不同,DOMContentLoaded
支持在页面下载的早期添加事件处理程序,这也就意味着用户能够尽早地用于页面进行互动。
④readystatechange事件
该事件的目的是提供与文档或元素的加载状态有关的信息。
支持readystatechange
事件的每个对象都有一个readystate
属性,属性值如下:
uninitialized
(未初始化):对象存在但尚未初始化;loading
(正在加载):对象正在加载数据;loaded
(加载完毕):对象加载数据完成;interactive
(交互):可以操作对象,但还没有完全加载;complete
(完成):对象已经加载完毕;
并非所有对象都会经历
readyState
的这几个阶段。即如果某个阶段不适用某个对象,则该对象完全可能跳过该阶段;并没有规定哪个阶段适用于哪个对象。
⑤pageshow和pagehide事件
“往返缓存”(back-forward cache,bfcache),该特性可以在用户使用浏览器的“后退”和前进按钮时加快页面的转换速度。这个缓存不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面保存在了内存里。
如果页面位于bfcache
中,那么再次打开该页面就不会触发load
事件。
pageshow
:该事件在页面显示后触发,无论页面是否来自bfcache
;该事件的event
事件包含一个名为persisted
的布尔值属性。如果该页面被保存在bfcache
中,则为true
;否则为false
。pagehide
:该事件会在浏览器卸载页面时触发,而且是在unload
事件之前触发。
以上俩个事件的目标都是
document
,但其事件处理程序必须要添加到window
对象中。
⑥hashchange事件
hashchange
事件处理程序必须添加给window
对象,然后URL参数列表只要有变化就会调用它。
此时的event
对象额外包含俩个属性:oldURL
、newURL
。这俩个参数分别保存着参数列表变化前后的完整URL。
8.设备事件
orientationchange
:设备查看模式
MozOrientation
:设备平面方向改变
deviceorientation
:设备空间方向变化
devicemotion
:设备是否移动
9.触摸与手势事件
触摸事件
touchstart
、touchmove
、touchend
、touchcancel
、touches
、targetTouches
、changeTouches
手势事件
gesturestart
、gesturechange
、gestureend
五、内存和性能
每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
1.事件委托
事件委托利用事件冒泡,指定的一个事件处理程序,就可以管理某一类型的所有事件。
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
<script type="text/javascript">
var list = document.getElementById("myLinks");
EventUtil.addHandler(list,"click",function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch(target.id){
case "doSomething":
document.title = "change the document title";
break;
case "goSomewhere":
location.href = "http://www.baidu.com";
break;
case "sayHi":
alert("hi");
break;
}
});
</script>
2.移除事件处理程序
内存中留有那些过时不用的"空事件处理程序",也是造成Web应用程序内存和性能问题的主要原因。
所有在不需要的时候移除事件处理程序,也是解决的一种方案。
<div id="myDiv">
<input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function(){
//执行某些操作
btn.onclick = null; //移除事件处理程序
document.getElementById("myDiv").innerHTML = "Processing...";
};
</script>
一般来说,最好的做法就是在页面卸载之前,先通过
onunload
事件处理程序移除所有事件处理程序。
六、模拟事件
1.DOM中的事件模拟
可以在document
对象上使用createEvent()
方法创建event
对象。
这个方法接收一个参数,即表示要创建的事件类型的字符串。
字符串参数可以是一下值:
UIEvents
:一般的UI事件;MouseEvents
:一般化的鼠标事件;MutationEvents
:一般化的DOM变动事件;HTMLEvents
:一般化的HTML事件;
dispatchEvent()
方法,所有支持事件的DOM节点都支持这个方法。调用该方法时,需要传入一个参数,即表示要触发事件的event
对象。
2.IE中的事件模拟
调用document.createEventObject()
方法可以在IE中创建event
对象。