一:事件流
事件冒泡:IE的事件流,事件开始时由具体的元素接收,然后逐级传播到较为不具体的节点。
事件捕获:“DOM2级事件”规范要求事件从document对象开始传播,但浏览器都是从window对象开始捕获事件。
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
在DOM事件流中,实际的目标在捕获阶段不会接收到事件,下一阶段是“处于目标”阶段,并在事件处理中被看成是冒泡阶段的一部分。
即使“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但主要浏览器都会在捕获阶段触发事件对象上的事件。结果,就有两个机会在目标对象上面操作事件。
事件处理程序
事件就是用户或浏览器自身执行的某种动作,诸如click、load和mouseover,都是事件的名字,事件处理程序的名字以“on”开头。
1、DOM0级事件处理程序
一是简单;二是具有跨浏览器的优势
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(this.id); //"myBtn"
};以这种方式添加的事件处理程序会在事件流的冒泡阶段处理。
2、DOM2级事件处理程序
“DOM2级事件”定义了两个方法,用于处理和删除事件处理程序的操作:addEventListener()和removeEventLister()。所有DOM节点都包含这两个方法,并且都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;是false,表示在冒泡阶段调用事件处理程序。
var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);与DOM0级方法一样,这里添加的事件处理程序也是在其依附的元素作用域中运行。使用DOm2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()添加的匿名函数将无法移除。
大多数情况下都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。
3、IE事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称和事件处理程序函数。。由于IE8及更早版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert(this === window); //true
});attachEvent()的第一个参数是“onclick”。在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域,在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert("Clicked");
});
btn.attachEvent("onclick",function(){
alert("hello world");
});如上所示,这里调用了两次attachEvent(),不过与DOM不同的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。单击这个例子的按钮,首先看到的是“hello world”,然后才是“Clicked”。
使用attachEvent()添加的事件可以通过detachEvent()来移除,必须提供相同的参数。
4、跨浏览器的事件处理程序
第一个要创建的方法是addHandler(),它的职责是视情况分别使用DOM0级方法、DOM2级方法或IE方法来添加事件,方法属于EventUtil的对象。addHandler()方法接受3个参数:要操作的元素、事件名称和事件处理程序函数。
var EventUtil = {
addHandler: function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type] = handler;
}
},
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type] = null;
}
}
}
这两个方法都会先检测传入的元素中是否存在DOM2级方法,在检测IE方法,最后一种可能是使用DOM0级方法。
事件对象
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。
1、DOM中的事件对象
兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click",function(event){
alert(event.type); //"click"
},false);
要阻止特定事件的默认行为,可以使用preventDefault()方法
var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
};stopPropagation()方法用于立即停止事件在DOM层次的传播,取消进一步的事件捕获或冒泡。例如,直接添加到一个按钮的事件处理程序可以调用stopPropagation(),从而避免触发在document.body上面的事件处理程序。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("Clicked");
event.stopPropagation();
};
document.body.onclick = function(event){
alert("Body clicked");
};事件对象的eventPhase属性,可以用来确定事件当前正处于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么eventPhase等于1;如果事件处理程序处于目标对象,则是2;如果处于冒泡阶段,等于3.
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"
};在此,我们通过window.event取得了event对象,并检测了被触发事件的类型(IE中的type属性与DOM中的type属性是相同的)。
如果事件处理程序使用attachEvent()添加的,那么就会有一个event对象作为参数被传入事件处理程序函数中,如下:
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick",function(event){
alert(event.type); //"click"
});像这样在使用attachevent()的情况下,也可以通过window对象来访问event对象。
3、跨浏览器的事件对象
可以对前面介绍的EventUtil对象加以增强。
- 内存和性能
在JS中,添加到页面的事件处理程序数量将直接关系到页面的整体运行性能。导致的原因:每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。
1、事件委托
事件委托利用了事件冒泡;使用事件委托利用了事件冒泡,只需在DOM树中尽量最高的层次上添加一个事件处理程序。
优点:
- document对象很快就可以访问,而且可以在页面生命周期的任何点上为它添加事件处理程序
只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少
- 整个页面占用的内存空间更少,能够提升整体性能
2、移除事件处理程序
每当将事件处理程序指定给元素时,运行的浏览器代码与支持页面交互的JS代码之间就会建立一个连接。在不需要的时候移除事件处理程序,内存中留有那些过时不用的“空事件处理程序”,也是造成Web应用程序内存与性能问题的主要原因。
两种情况可能造成上述问题:
一是从文档中移除带有事件处理程序的元素时,可能是通过纯粹的DOM操作。
二是卸载页面的时候,没有清理干净事件处理程序。