1.事件流
当我们点击网页上的某一个元素时,触发的动作不仅仅跟这个元素有关,还跟包含这个元素的所有上层容器有关。触发事件流的流动有一个先后顺序,目前有两种:1.IE所提出的事件冒泡;2.Netscape所提出的事件捕获。
DOM事件流则是综合了前面两种事件流,先捕获再冒泡。现代浏览器大多支持DOM事件流。
2.事件处理程序
为元素绑定相应的事件处理程序有3种方法:HTML、DOM0级、DOM2级。事件处理程序以”on”开头,如”click”事件的处理程序是”onclick()”。
2.1 HTML事件处理
我们把事件处理程序当做html的元素属性加入html标签中。这种方法的最大缺点就是html和js紧耦合。
<input type="button" value="button" onclick="alert(`Click Me`)" />
<input type="button" value="button" onclick="test()" />
<script>
function test() {
//...
}
</script>
2.2 DOM0级事件处理
通过DOM操作获取元素,元素都带有事件处理(比如onclik()
)的属性,我们把函数赋值给该属性即可。在事件处理程序中,this指向给调用该事件处理程序的元素(在事件委托中,this就指向那个尽量高层次的元素)。若想删除事件处理程序,将该属性赋值null即可。DOM0级绑定的事件处理程序会发生在冒泡阶段。
var p = document.getElementById('p');
p.onclick = function() {
alert(this.id);//p
}
p.onclick = null;//删除绑定的事件处理程序
2.3 DOM2级事件处理
DOM2级事件处理程序的功能比DOM0级更加丰富。DOM2级可以一个事件添加多个处理程序,还可以指定程序在冒泡阶段调用还是捕获阶段调用。DOM2级要删除处理程序,需要传入添加函数时的那个函数参数,这就说明,添加的匿名函数无法删除。
var p = document.getElementById('p');
p.addEventListener("click", function(){
alert(this.id);//p
}, false);//第三个参数 true:捕获阶段调用;false:冒泡阶段调用;
//该移除函数没有起作用
p.removeEventListener("click", function(){
alert(this.id);
}, false);
2.4 IE事件处理
虽然IE9也支持DOM2级事件处理程序(DOM事件流),但在IE9以前,是只支持IE事件处理(IE事件流)
的。IE事件流是事件冒泡。IE事件处理程序与DOM2级事件处理程序非常相似。IE事件处理只支持两个参数,第三个参数无法选择捕获还是冒泡。在IE8以及更早版本中,只支持冒泡。与DOM2级不同的是,这里的this指向全局window。
这里我们注意,并不是所有IE浏览器都是IE事件处理(IE事件流)
,像IE9就支持DOM事件流
,支持DOM事件流,也支持DOM2级事件处理程序。
var p = document.getElementById('p');
p.attachEvent("onclick", function(){//注意这里是"onclick",而不是DOM2级的"click"
alert(this === window);//true
});
//与DOM2级一样,匿名函数无法被删除
p.detachEvent("onclick", function(){
alert(this === window);//true
});
3.DOM事件对象
事件对象分为DOM事件对象和IE事件对象,IE就是个奇葩的存在,除了IE之外,其他浏览器都是DOM事件对象。
事件处理程序会接收一个事件对象,该事件对象中有许多跟事件有关的属性,比如type、churentTarget、target等。
下面我们区分一下三个容易混淆的属性this
、target
、currentTarget
。this始终等于currentTarget,这两个又等于调用处理程序的元素。target等于动作真实发生的对象。
<body>
<div id='div'>
<p id='p'>hello!</p>
</div>
</body>
//div是p的父元素,如果点击p,event.target就是p
//如果点击div其他地方(不是p),event.target就是div
div.detachEvent("onclick", function(event){
//event.type == "click"
alert(this === div);//true
alert(event.currentTarget === div);//true
alert(event.target === p);//true
});
4.事件代理(事件委托)
如果我们有许多的元素需要绑定事件处理程序,按照上面的方法:先使用DOM获取元素,再给元素绑定函数。这种方法会使得web性能下降:DOM操作耗时;函数的本质是对象,耗内存。
这种场景的解决方法就是使用事件代理,即利用事件冒泡,给尽量高层次的元素绑定一个事件,在这个事件中,根据事件对象的属性来做相应的处理。
div.onclick = function(event) {
switch(event.target.id) {
case 'p':
alert('p');
break;
case 'div':
alert('div');
break;
}
}