定义:
- 事件:用户或浏览器自身执行的某种动作。(click,load,mouseover)
- 事件流:从页面中接收事件的顺序,也可理解为事件在页面中传播的顺序。
- 事件处理程序:响应某个事件的函数。均以on开头。(onclick,onmouseover等)
1.事件流
事件流分成两种:事件冒泡和事件捕获:
- IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点。
- 事件捕获的思想恰恰相反:不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。(在事件达到预定目标之前捕获它)
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。首先发生的是事件捕获阶段,为截获事件提供了机会。然后是实际的目标接收事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件作出相应。
图片来源于网络以上图为例,单击div会按照图上显示的顺序触发事件。
浏览器支持
- IE9,safari,chrome等目前均支持事件捕获的事件流类型,这些浏览器都是从windows对象开始捕获事件的。
- 大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容浏览器。(IE8及更早版本不支持时间捕获)如果不是特别需要,不建议在事件捕获阶段注册事件处理程序。
- IE9,Opera,Firefox,Chrome 和 Safari都支持DOM事件流,IE8及更早版本不支持DOM事件流。
例子
addEventListener
在写例子之前,先简单介绍一个方法:addEventListener
- 该方法是‘DOM2级事件处理程序’的方法,它接受三个参数:要处理的事件名,作为事件处理程序的函数和一个布尔值
- 布尔值为false代表在冒泡阶段调用事件处理程序,true代表在捕获阶段调用事件处理程序
- 比如:btn.addEventListener("click",function(){alert("Hello world")},false) 就代表给按钮btn注册一个事件,当被点击时浏览器会弹出消息“Hello World”,并且在冒泡阶段调用click。
比较事件冒泡和事件捕获
接下来就用addEventListener来展示事件冒泡和事件捕获的区别:
先放上html 和 css 代码:
<div id="level1">
<div id="level2">
<div id="level3"></div>
</div>
</div>
#level1{
background:red;
height:200px;
width:200px;
}
#level2{
background:orange;
height:100px;
width:100px;
}
#level3{
background:yellow;
height:50px;
width:50px;
}
呈现的效果很简单,就是三个不同颜色的div块:
事件流对比.png
然后用JS给每一个div注册一个点击事件,为了更加直观,当每一个div的点击事件被触发时,会显示一条该div块背景颜色的消息,这样我们就能清楚地看到各个div的click事件被触发的先后顺序
var level1=document.getElementById('level1');
var level2=document.getElementById('level2');
var level3=document.getElementById('level3');
level.addEventListener('click',function(){
alert('yellow');
},false);
leve2.addEventListener('click',function(){
alert('orange');
},false);
leve3.addEventListener('click',function(){
alert('red');
},false);
根据上面的例子,addEventListener的布尔值参数被设置为false,所以会在冒泡阶段调用事件处理程序。当点击黄色方块后,会先后弹出消息"yellow","orange","red";点击橙色方块后,会弹出消息"orange","red";点击红色方块后,会弹出"red".这个结果也印证了我们之前所说的事件冒泡会使事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点。这里的黄色方块是嵌套在最里面的div块,所以是该例子中最具体的节点,红色块是最外层的div快,在该例中是最不具体的节点。
接着我们再测试一下用事件捕获的思想会获得什么结果:
html和css代码都不需要变动,只需要对JS代码稍作修改:把布尔值从false改成true.
var level1=document.getElementById('level1');
var level2=document.getElementById('level2');
var level3=document.getElementById('level3');
level.addEventListener('click',function(){
alert('yellow');
},true);
leve2.addEventListener('click',function(){
alert('orange');
},true);
leve3.addEventListener('click',function(){
alert('red');
},true);
结果还是和我们预想的一样,当点击黄色方块后,会先后弹出消息"red","orange","yellow";当点击橙色方块后,会先后弹出消息"red","orange";点击红色方块时仍然只有"red".结果和上例恰好相反。
从上述两个例子,我们可以清晰地看出事件冒泡和事件捕获两种事件流的区别。
事件处理程序
4种为事件指定处理程序的方式:
- HTML 事件处理程序:
<input type="button" value="click" onclick="showMsg()"/>
function showMsg(){
alert("hello world");
}
- DOM0级事件处理程序:
btn.onclick=function(){
alert("hello world");
};
- DOM2级事件处理程序:
btn.addEventListener("click",function(){},false); //事件冒泡
btn.addEventListener("click",function(){},true); //事件捕获
- IE事件处理程序
- 通过attachEvent()添加的事件处理程序会被添加到冒泡阶段。
- 使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。
btn.attachEvent("onclick",function(){
alert("hello world");
});
事件代理(event delegation)
定义
事件代理允许我们需要为很多元素添加事件的时候,可以将事件添加到它们的父节点而将事件委托给父节点来触发处理函数,这得益于事件冒泡机制。
举例
html 代码如下:
<ul id="parent">
<li id="child1">item1</li>
<li id="child2">item2</li>
<li id="child3">item3</li>
<li id="child4">item4</li>
<li id="child5">item5</li>
</ul>
Javascript代码如下:
var parent=document.getElementById("parent");
parent.addEventListener("click",function(event){
if(event.target&&event.target.nodeName=="LI"){
alert("list item "+event.target.id+" was clicked");
}
});
这里我们假设需要为每一个Li节点添加一个点击事件,如果依次添加,尤其是如果你需要在程序内不同的地方添加和删除点击事件,那么就会非常麻烦。其中一个解决方法便是将点击事件添加到LI节点的父元素UL节点上,如上述代码所示,当事件冒泡到父节点以后,通过target属性可以得知哪一个LI节点被点击了,从而进行相应的处理。此例只是将获取的LI元素的ID显示出来了。
jQuery中的delegate()
delegate()方法为指定的元素(被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数,该方法有四个参数,分别是选择器,事件类型,传递到函数的数据,函数,传递到函数的数据可以选填。
举例
还是上面那个例子,html代码不变,jQuery代码如下:
$("#parent").delegate("li","click",function(){
$(this).after("clicked");
})
为什么要用事件代理?
事件代理只需要在父元素绑定一次所需事件,而不需要在子元素上绑定多次,从而大大提升了性能,也提升了编码效率。
参考
- Javascript高级程序设计
- How Javascript Event Delegation Works
网友评论