一、事件流
事件流描述的是从页面中接收事件的顺序,IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。
1、事件冒泡
事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。以下面的HTML页面为例:
<html>
<head>
<title>山水子农</title>
</head>
<body>
<div>Click Me</div>
</body>
</html>
如果你单击了页面中的<div>元素,那么这个click事件会按照如下顺序传播:
div -> body -> html -> document
对于冒泡流的事件流机制,存在如下的兼容问题:
IE5.5及更早版本: div -> body -> document
IE5.5之后到IE9之前: div -> body -> html -> document
IE9、Firefox、Chrome和Safari: div -> body -> html -> document -> window
<html>
<head>
<title>事件冒泡</title>
<meta charset="utf-8">
<body id='body'>
<div id="box">
<input type="button" value="按钮" id="btn">
</div>
<script>
function bodyz(){
alert('我是body');
}
function boxz(){
alert('我是div');
}
function btnz(event){
alert('我是input');
//event.stopPropagation();阻止事件冒泡之后点击按钮就不会出现‘我是div’、‘我是body’
}
var body=document.getElementById('body');
var box=document.getElementById('box');
var btn=document.getElementById('btn');
box.addEventListener('click',boxz,false);
btn.addEventListener('click',btnz,false);
body.addEventListener('click',bodyz,false);
</script>
</body>
</head>
点击按钮会依次弹出:‘我是input’、‘我是div’、‘我是body’
2、事件捕获
事件开始的时候由最不具体的节点接收,然后逐级向下传播到最具体的节点。事件捕获的用意在于在事件达到预定目标之前捕获它。以上面的实例来看,click事件的执行顺序为:document -> html -> body -> div
虽然事件捕获是Netscape Communicator唯一支持的事件流模型,但IE9、Firefox、Chrome、Opera和Safari目前也都支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件的。
由于老版本的浏览器不支持,因此很少有人使用事件捕获。我们也建议大家放心地使用事件冒泡,在有特殊需要时在使用事件捕获。
3、DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供机会。然后实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
二、事件处理程序
1、HTML事件处理程序
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script>
function showMessage(){
alert("HTML 事件处理程序");
}
</script>
<title>HTML 事件处理程序</title>
</head>
<body>
<input type="button" value="Click Me1" οnclick="alert('HTML 事件处理程')"/>
<input type="button" value="Click Me2" οnclick="showMessage()"/>
</body>
</html>
在HTML中指定事件处理程序有两个缺点:
(1)、存在一个时差问题。因为用户可能会在HTML元素出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件。
(2)、HTML与JavaScript代码紧密耦合。如果要更换事件处理程序,就要改动两个地方:HTML代码和JavaScript代码。而这正是许多开发人员放弃HTML事件处理程序,转而使用JavaScript指定事件处理程序的原因所在。
2、DOM0级事件处理程序
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代Web浏览器中出现的,而且至今仍然为所有现代浏览器所支持。原因一是简单,二是具有跨浏览器的优势。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>DOM0级事件处理程序</title>
</head>
<body>
<input type="button" value="Click Me" id="myBtn"/>
<script>
var btn=document.getElementById("myBtn");
btn.οnclick=function(){
alert("DOM0级事件处理程序");
setTimeout(function(){
btn.οnclick=null;//删除事件处理程序
alert("删除事件处理程序");
},200);
}
</script>
</body>
</html>
3、DOM2级事件处理程序“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有的DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>DOM2级事件处理程序</title>
</head>
<body>
<input type="button" value="Click Me" id="myBtn"/>
<script>
var btn=document.getElementById("myBtn");
btn.addEventListener("click",function(){//click不要on,三个参数。
alert("DOM2级事件处理程序1");
},false);
btn.addEventListener("click",function(){
alert("DOM2级事件处理程序2");
},false);
//先输出DOM2级事件处理程序1后DOM2级事件处理程序2
</script>
</body>
</html>
DOM0和DOM2级事件处理程序都有一个共同的优点就是可以同时添加多个事件处理程序。使用removeEventListener()移除addEventListener()事件时,移除时传入的参数与添加处理程序时使用的参数相同。<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>DOM2级事件处理程序</title>
</head>
<body>
<input type="button" value="Click Me" id="myBtn"/>
<script>
var btn=document.getElementById("myBtn");
var handler=function(){
alert("DOM2级事件处理程序");
setTimeout(function(){
btn.removeEventListener("click",handler,false);
alert("删除事件处理程序");
},1000);
};
btn.addEventListener("click",handler,false);
</script>
</body>
</html>
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。
IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>IE事件处理程序</title>
</head>
<body>
<input type="button" value="Click Me" id="myBtn"/>
<script>
var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(){//click要加on,两个参数。
alert("IE事件处理程序1");
});
btn.attachEvent("onclick",function(){
alert("IE事件处理程序2");
});
//先输出IE事件处理程序2后IE事件处理程序1.
</script>
</body>
</html>
使用detachEvent()移除attachEvent()事件时,移除时传入的参数与添加处理程序时使用的参数相同。<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>IE事件处理程序</title>
</head>
<body>
<input type="button" value="Click Me" id="myBtn"/>
<script>
var btn=document.getElementById("myBtn");
var handler=function(){
alert("IE事件处理程序");
setTimeout(function(){
btn.detachEvent("onclick",handler);
alert("删除事件处理程序");
},1000);
};
btn.attachEvent("onclick",handler);
</script>
</body>
</html>
5、跨浏览器的事件处理程序
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>跨浏览器的事件处理程序</title>
</head>
<body>
<input type="button" value="Click Me" id="myBtn"/>
<script>
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){//DOM2级
element.addEventListener(type,handler,false);
}else if(element.attachEvent){//IE
element.attachEvent("on"+type,handler);
}else{
element["on"+type]=handler;//DOM0级 <span style="font-family: Arial, Helvetica, sans-serif;">element[onclick]</span><span style="font-family: Arial, Helvetica, sans-serif;"> ===element.onclick</span>
}
},//此处的","千万别忘记,EventUtil是对象。
removeHandler:function(element,type,handler){
if(element.removeEventListener){//DOM2级
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){//IE
element.detachEvent("on"+type,handler);
}else{
element["on"+type]=null;//DOM0级
}
}
}
var btn=document.getElementById("myBtn");
var handler=function(){
alert("跨浏览器的事件处理程序");
setTimeout(function(){
EventUtil.removeHandler(btn,"click",handler);
alert("删除事件处理程序");
},1000);
};
EventUtil.addHandler(btn,"click",handler);//事件类型不要加"on"
</script>
</body>
</html>