本博客分三部分来阐述事件:DOM事件流、事件处理程序、事件对象。
一、DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
(一)事件捕获阶段,document对象首先接收到事件,然后事件沿着DOM树一次往下,一直传播到事件的实际目标;
(二)处于目标阶段,即事件在实际目标上发生;
(三)事件冒泡阶段,事件从实际目标开始,沿着DOM树一级一级往上传播,直至document对象。
PS. 虽然“DOM2 级事件”规范明确要求捕获阶段不会涉及事件目标,但是多数支持DOM事件流的浏览器都实现了一种特定的行为:IE9、Safari、Chrome、Firefox 和Opera 9.5 及更高版本都会在捕获阶段触发事件对象上的事件。这样,就是有两个机会(事件捕获阶段和事件冒泡阶段)在目标对象上面操作事件。
二、事件处理程序
事件处理程序,共有三种,一是HTML事件处理程序,二是DOM0级事件处理程序,三是DOM2级事件处理程序。
(一)HTML事件处理程序:指在HTML标签中,使用一个与相应事件处理程序同名的HTML特性来指定的事件处理程序,如:
这种方式是不推荐的,因为导致了HTML与JS高度耦合。
(二)DOM0级事件处理程序:这是通过JavaScript指定事件处理程序的传统方式,即将一个函数赋值给一个事件处理程序属性。如:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(this.id);//myBtn
};
删除通过DOM0级方法指定的事件处理程序,只要将事件处理程序属性的值设置为null即可。如:btn.onclick = null;
注意事项:DOM0级只能为一个元素添加一个事件处理程序。
(三)DOM2级事件处理程序:与DOM0级相比,DOM2级可以为一个元素添加多个事件处理程序。
DOM2级事件处理程序,定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener() 和 removeEventListener()。比如,在某个元素上为click事件添加/删除事件处理程序:
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
//这里省略了其他代码
btn.removeEventListener("click", handler, false); //有效!
可以看到addEventListener() 和 removeEventListener() 都接收三个入参,第一个入参是事件名称,第二个参数是一个函数,第三个参数是一个boolean值。第三个参数是true时,表示在捕获阶段调用事件处理程序;第三个参数是false时,表示在冒泡阶段调用事件处理程序。
注意事项:
1)通过addEventListener() 添加的事件处理程序,只能通过调用 removeEventListener() 方法删除;
2)调用 removeEventListener() 方法删除某个事件处理程序时,参数必须和 addEventListener() 的参数完全一致,否则无法删除。所以,如果给第二个参数传入匿名函数,这个事件处理程序将无法删除。
三、事件对象
兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0 级或 DOM2 级),都会传入event 对象。event对象有几个属性和方法需要了解的:
(一)target、currentTarget属性
target属性,指向事件的实际目标;currentTarget指向事件处理程序所依附的目标。看下面的例子:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script>
window.onload = function(){
var div = document.getElementById("A2");
document.body.onclick = function(event){
console.log(event.target);
console.log("----------------");
console.log(event.currentTarget);
}
}
</script>
</head>
<body>
<div id="A2" style="background:black;border: 1px solid blue;height: 200px;width: 400px;">
</div>
</body>
</html>
在点击ID为A2的div块后,打印结果如下图所示:
例子中,通过DOM0级的方式为body元素绑定了一个事件处理程序,所以console.log(event.currentTarget);
语句会打印出body标签的内容。
另外,在事件处理程序内部,this 永远与 event.currentTarget 指向同一个对象。可以通过console.log(event.currentTarget === this);
验证。
(二)preventDefault() 方法和 stopPropagation() 方法
preventDefault() 方法,用于阻止特定事件的默认行为,例如<a>
标签的默认行为就是在被单击时会导航到其href 特性指定的URL。如果想阻止链接导航这一默认行为,那么通过链接的onclick 事件处理程序可以取消它。如下:
var link = document.getElementById("myLink");
link.onclick = function(event){
event.preventDefault();
};
stopPropagation() 方法,用于立即停止事件在DOM 层次中的传播,即取消进一步的事件捕获或冒泡。看下面的例子:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script>
window.onload = function(){
var div = document.getElementById("A2");
div.onclick = function(event){
alert("div被点击了!");
event.stopPropagation();
}
document.body.onclick = function(event){
alert("Body Clicked!");
}
}
</script>
</head>
<body>
<div id="A2" style="background:black;border: 1px solid blue;height: 200px;width: 400px;">
</div>
</body>
</html>
上面的代码中,如果点击div块,只会弹出一个警告框,显示信息”div被点击了!”。而如果把event.stopPropagation();
这一行代码删除,则会弹出两个警告框。
需要注意的是,当event的bubbles属性为true时,stopPropagation()方法才会起作用。
四、喜欢搞事情的IE浏览器
IE内核的浏览器在国内的用户还是蛮多的,所以我们还是要了解下搞事情的IE浏览器到底搞了些什么事情。
(一)IE浏览器在事件处理程序搞的事情
IE 浏览器实现了与DOM 中类似的两个方法:attachEvent() 和 detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。注意:通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
使用 attachEvent() 添加的事件可以通过 detachEvent() 来移除,条件是必须提供相同的参数。与DOM 方法一样,这也意味着添加的匿名函数将不能被移除,如上面例子中的事件处理程序无法通过 detachEvent() 移除。需要写成下面的形式,才可正常地移除事件处理程序:
var btn = document.getElementById("myBtn");
var handler = function(){
alert("Clicked");
}
btn.attachEvent("onclick", handler);
//省略其他代码
btn.detachEvent("onclick", handler);
(二)IE浏览器在事件对象上搞的事情
IE浏览器在事件对象上搞的事情,主要有两个方面,一个是访问event对象的方式,另一个是event对象的属性。
IE浏览器中,访问event对象的方式,取决于指定事件处理程序的方法。
(1)在使用DOM0 级方法添加事件处理程序时,需要通过window.event来访问event对象;
(2)在使用attachEvent()添加事件处理程序时,可以直接访问event对象;
(3)通过HTML特性指定的事件处理程序时,可以直接通过event变量访问event对象。
IE浏览器中的event对象的属性也和其他兼容DOM的浏览器的event对象的属性不同。如下:
(1)srcElement属性:同DOM中的target属性;温馨提示:在IE浏览器中,最好使用srcElement属性来指定当前目标,而不要使用this。
(2)returnValue属性:值为boolean型,默认为true,当设置为false时,会取消事件的默认行为,与DOM中的 preventDefault() 方法的作用相同;
(3)cancelBubble属性:值为boolean型,默认为false,当设置为true时,会取消事件冒泡,与DOM中的 stopPropagation() 方法的作用相同。
五、跨浏览器的事件处理
这里分享一个跨浏览器的事件处理程序,代码如下:
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;
}
},
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;
}
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};