前言

关于JavaScript中的事件,我们首先会想到的就是两种事件流:事件冒泡和事件捕获,事件流在前端面试中也问的比较多,但是从我工作的实际经验来看,事件冒泡和事件捕获的区分在工作中涉及的比较少。反倒是为事件指定处理程序用的非常多。今天我就把事件处理程序总结一下。

概念

事件处理程序就是当指定的事件发生时的处理方式,比如说我们在网页上点击一个按钮,弹出了一个弹窗,这时候 点击 就是事件,而 弹出一个弹窗 就是相对应的事件处理程序。

简单吧?那么我们怎么为一个事件添加事件处理程序呢?以前我并没有总结过,只知道有那么几种方式,总结了之后,才对事件以及事件处理程序有一个完整的了解。

事件处理程序

事件处理程序按照指定方式的不同可以分为:

  1. HTML事件处理程序
  2. DOM0级事件处理程序
  3. DOM2级事件处理程序

在DOM0级/DOM2级事件处理程序中,又涉及到IE/非IE事件处理程序。

HTML事件处理程序

HTML事件处理程序就是在HTML代码里为元素指定事件处理程序.

1
2
3
4
5
6
7
<input type="button" value="点击" onclick="alert('test')">
<input type="button" value="点击2" onclick="set()">
<script>
function set(){
//这时候this指向input这个元素,也就是事件的目标元素
}
</script>

这种方式有两个很明显的缺点:

  1. 当需要为多个同样的元素绑定相同的事件处理程序时,需要重复添加onclick="alert('test')"这段代码,很明显这样是很麻烦的,并且也违背了将HTML与JavaScript分离的原则。
  2. 如果用户在DOM内容加载之后,js文件加载之前点击了按钮,那就会报错。
DOM0级事件处理程序

DOM0级事件处理程序是先获取元素,然后为元素指定事件处理程序

1
2
3
4
5
6
7
8
9
10
11
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
alert('a');
// this指向btn
}
btn.onclick = function(){
alert('b');
}
</script>

这种方式就避免了上文提到的DOM0级事件处理程序的两个问题,但是又有新的问题。当为一个元素指定了两个及多个同类型的事件处理程序后,后面的会将前面的覆盖。在上面的例子中,点击按钮,之后只会弹出b,虽然说这种情况比较少(如果有的话也可以通过把程序写在一个函数里来避免),但终究不是好的解决方法。

想要删除事件处理程序的话只需要使用btn.onclick = null,这时候就会把为btn指定的所有click事件全部去掉,很明显,这也不是一种好的处理方式。

DOM2级事件处理程序

DOM2级事件处理程序有两个方法:addEventListener()removeEventListener()。分别用于绑定事件和解除绑定。使用方法和DOM2级事件处理程序类似,先获取元素然后指定事件。

1
2
3
4
5
6
7
8
9
10
11
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById('btn');
btn.addEventListener('click',function(){
alert('test');
// this等于btn
},false);
btn.addEventListener('click',function(){
alert('test');
},false);// 无法解除绑定,因为处理程序是一个匿名函数
</script>

这种方法相比DOM0级事件处理程序,好处是可以为同一个元素绑定多个相同类型的事件,不会彼此覆盖。

1
2
3
4
5
6
7
8
9
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById('btn');
function test(){
alert('test');
}
btn.addEventListener('click',test,false);
btn.removeEventListener('click',test,false); //这样才能接触绑定,因为事件处理程序是同一个函数
</script>

这两个函数的最后一个参数都必须要有,表示调用事件处理程序的时间,为true表示在捕获阶段调用,为fasle表示在冒泡阶段调用。不过在我实际的工作中,很少需要特意区分冒泡和捕获,一般都是把最后一个参数设置为false。

IE事件处理程序

IE中有两个事件处理程序:attachEvent()detachEvent(),其使用形式和DOM2级的事件处理程序类似,值得注意的是设置事件名的时候需要在事件名前加on.

1
2
3
4
5
6
7
8
<input type="button" value="点击" id="btn">
<script>
var btn = document.getElementById('btn');
btn.attachEvent("onclick",function(){
alert('test');
//this等于全局变量window
});
</script>

在使用detachEvent()解除绑定时,用法和removeEventListener()一样,无法解除绑定的匿名函数处理程序,只能解除通过相同参数设置的处理程序。

需要注意的是:不要因为IE自己定义了两个方法,就认为DOM2级的方法在IE中不能用,DOM2级的方法在IE9+中都是可以使用的。

事件在工作中的实际使用

在工作中一般都使用了某个JavaScript框架,框架本身提供了绑定事件的方法。这个时候的原则是:能使用 事件代理 就使用 事件代理 。事件代理的好处在我看来主要有两个:

  1. 防止了可能因为资源加载的问题找不到元素而报错的情况。
  2. 为多个同类型元素绑定同类型事件时不需要一个一个绑定。只需要绑定一次就行。