何谓事件?
即指文档或浏览器窗口发生的一些特定交互的瞬间。我们可以用侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。
事件流
描述从页面接收事件的顺序。IE和Netscape开发团队居然提出了几乎完全相反的事件流概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。下面就简要介绍一下事件冒泡和事件捕获。
事件冒泡
IE的事件流叫做事件冒泡,即事件开始时,由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>
当你点击了页面上的div元素,那么这个click时间将会按照如下顺序传播:
click事件首先会在
所有的现代浏览器都支持事件冒泡,但是在具体实现是还是会有差别。
事件捕获
Netscape Communicator团队提出的另一种事件流叫做事件捕获。事件捕获的思想是不太具体的节点应该更早接收事件,而具体的节点应该最后接收到事件。其用意在于事件到达预定目标之前捕获它。虽然事件捕获是Netscape Communicator唯一支持的事件流模型,但是IE9,Safari,Chrome,Opera和Firefox目前也都支持这种事件流模型。
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。
在DOM事件流中,实际的目标在捕获阶段是不会接收到事件。以上文事件冒泡的例子为例:
在捕获阶段,事件从document到再到后就停止了。下一阶段是“处于目标的阶段”于是事件在
事件处理程序
HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与之相应事件处理程序同名的html特性来指定。
这样指定事件处理程序首先会创建一个封装着元素属性的函数。这个函数中有一个局部变量event,也就是事件对象。例如:
<input type="button" value="Click Me" onclick="alert(event.type)" name="">//click
<input type="button" value="Click Me" onclick="alert(event.type)" name="">//click
通过event变量可以直接访问事件对象,不用自己定义,也不用从函数列表中读取。这个函数内部this值等于事件的目标元素。
HTML事件处理程序存在很多缺点:
1. 存在时差问题。用户可能在HTML元素一出现在页面上就触发相应的事件,但当时事件处理程序尚不具备执行条件。
2. 这样扩展事件处理程序的作用域链在不同的浏览器中会导致不同的结果。
3. HTML和Javascript代码紧密耦合。如要更换程序,则两者都需要修改。
DMO0级事件处理程序
通过JS指定事件处理程序的传统方式,将一个函数赋值给一个事件处理程序属性。例如:
window.onload = function(){
var btn = document.getElementById("mybutton");
console.log(btn);
btn.onclick = function(){
alert("haha");
};
};
使用DOM0级方法指定事件处理程序被认为是元素的处理方法,因此,这时候,事件处理程序是在元素的块级作用域中运行的,也就是说this引用的是当前元素。例如:
var btn2 = document.getElementById("mybutton1");
btn2.onclick = function(){
alert(this.type);//button(this表示的是当前的元素)
DOM2级事件处理程序
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有的DOM节点都包含这两种方法,并且它们都接受三个参数:要处理的事件名,作为事件处理程序的函数和一个布尔值。最后这个布尔值是true,表示在捕获阶段调用事件处理程序,如果是false,表示在冒泡阶段调用事件处理程序。例如:
var btn = document.getElementById("mybutton");
btn.addEventListener("click",function(){
alert(this.type);
},false);
因为最后一个参数为false,所以该事件会在冒泡阶段被触发。与DOM0级方法一样,这里的添加的事件处理程序是在其依附的元素的作用域中运行。使用DOM2级事件处理程序可以给元素添加多个事件处理程序。例如:
var btn = document.getElementById("mybutton");
btn.addEventListener("click",function(){
alert(this.type);
},false);
btn.addEventListener("click",function(){
alert("hello world");
},false);
点击按钮之后,两个事件处理程序会按照添加的顺序触发。
通过addEventListener()添加的事件处理程序只能用removeEventListener()来移除;移除时,传入的参数与添加处理程序时使用的参数一致。这也就意味着addEventListener()添加的匿名函数无法移除。例如:
var btn = document.getElementById("mybtn");
btn.addEventListener("click",function(){ alert(this.id);},false);
//省略了其他部分代码
btn.removeEventListener("click",function(){ alert(this.id);},false);
在上面的例子中,虽然调用了removeEventListener()函数,并且看似传入了相同的参数,但实际上,第二个参数与传入addEventListener()的函数是完全不同的。其函数作用域内部的this 指代的是该匿名函数本身。
我们可以将匿名函数赋值给一个变量:
var message = function(){
alert("hello world");
};
btn.addEventListener("click",message(),false);
btn.removeEventListener("click",message(),false);
这样我们就可以将事件处理程序移除
大多数情况下,是将事件处理程序添加到事件流的冒泡阶段,这样可以最大程度的兼容各种浏览器
IE事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。这两个方法接收相同的两个参数:事件处理程序名称与事件处理程序函数。由于IE8及更早的版本只支持冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域。在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域中运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this就相当于window。
与addEventListener()类似,attachEvent()方法也可以用来为一个元素添加多个事件处理程序。举例:
var btn = document.getelementById("myBtn");
btn.attachEvent("onclick",function(){alert("clicked");});
btn.attachEvent("onclick",function(){alert("hello world");});
这里调用了两次attachEvent(),为同一个按钮添加了两个不同的事件处理程序 。不过,与DOM方法不同是,这些事件处理程序不是以添加它们的执行程序执行,而是以相反的顺序被
跨浏览器的事件处理程序
window.onload = function(){
var EventUtil = {
//创建addHandler(),它的职责是视情况分别使用DOM0级方法,DOM2级方法或者IE方法来添加事件。这个方法属于一个名叫EventHandler的对象
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;
}
},
//与addHandler函数对应的方法是removeHandler(),它也接受相同的参数
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;
}
}
};
var btn = document.getElementById("mybutton");
var handler = function(){
alert("hello world");
}
EventUtil.addHandler(btn,"click",handler);
}