DOM事件

230 阅读7分钟

一:事件流

事件冒泡: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操作。

二是卸载页面的时候,没有清理干净事件处理程序。