一、基本概念
1、事件冒泡
IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。
2、事件捕获
Netscape Communicator团队提出的另一种事件流叫做事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。
3、DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
二、事件处理程序
1、HTML事件处理程序
<input type="button" value="Click me" onclick="alert('Clicked')"/>
在HTML中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本
<script type="text/javascript">
function showMessage(){
alert("Hello world");
}
<script/>
<input type="button" value="Click Me" onclick="showMessage()"/>
事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。
2、DOM0级事件处理程序
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序。
var btn=document.getElementById("myBtn");
btn.onclick=function(){
alert(this.id); //"myBtn"
};
btn.onclick=null; //删除事件处理程序
这时候的事件处理程序是在元素的作用域中运行,所以会返回myBtn。
3、DOM2级事件处理程序
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。
所有DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。
最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
var btn=document.getElementById("mybtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
btn.addEventListener("click",function(){
alert("Hello world");
},false);
这里添加的事件处理程序也是在其依附的元素的作用域中运行。使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。这两个事件处理程序会按照添加它们的顺序触发。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()添加的匿名函数将无法移除。
var btn=document.getElementById("myBtn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
//这里省略了其他代码
btn.removeListener("click",function(){ //没有用!
alert(this.id);
},false);
var btn=document.getElementById("myBtn");
var handler=function(){
alert(this.id);
};
btn.addEventListener("click",handler,false);
//这里省略了其他代码
btn.removeListener("click",handler,false); //有效
4、IE事件处理程序
attachEvent()和detachEvent(),只接受两个参数。通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window.
var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert(this===window); //true
});
var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(){
alert("Clicked"); //该事件后触发
});
btn.attachEvent("onclick",function(){
alert("Hello world!"); //该事件先触发
});
移除事件:
var btn=document.getElementById("myBtn");
var handler=function(){
alert("Clicked");
};
btn.attachEvent("onclick",handler);
//这里省略了其他代码
btn.detachEvent("onclick",handler);
5、跨浏览器的事件处理程序
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListerner(type,handler,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}else{
element["on"+type]=handler;
}
},
removerHandler: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;
}
}
};
可以像下面这样使用EventUtil对象:
var btn=document.getElementById("myBtn");
var handler=function(){
alert("Clicked");
};
EventUtil.addHandler(btn,"click",handler);
//这里省略了其他代码
EventUtil.removeHandler(btn,"click",handler);
三、事件对象
1、DOM中的事件对象
兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。
2、IE中的事件对象
与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。
3、跨浏览器的事件对象
IE中的event对象的全部信息和方法DOM对象中都有,只不过实现方式不一样。
var EventUtil={
addHandler:function(element,type,handler){
//省略的代码
},
getEvent:function(event){
return event?event:window.event;
},
getTarget:function(event){
return event.target||event.srcElement;
},
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},
removeHandler:function(element,type,handler){
//省略的代码
},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
};
1、使用第一个方法:
btn.onclick=function(event){
event=EventUtil.getEvent(event);
};
2、使用第二个方法:
btn.onclick=function(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
};
3、使用第三个方法:
var link=document.getElementById("myLink");
link.onclick=function(event){
event=EventUtil.getEvent(event);
EventUtil.preventDefault(event);
};
4、使用第四个方法:
var btn=document.getElementById("myBtn");
btn.onclick=function(event){
alert("Clicked");
event=EventUtil.getEvent(event);
EventUtil.syopPropagation(event);
};
document.body.onclick=function(event){
alert("Body clicked");
};
四、事件委托
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。以下面的HTML代码为例:
<ul>
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
使用事件委托,只需要在DOM树中尽量最高的层次上添加一个事件处理程序,如下面的例子所示:
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="I changed the document's title";
break;
case "goSomewhere":
location.href="http://www.wrox.com";
break;
case "sayHi":
alert("hi");
break;
}
});