参考链接:
JavaScript 事件委托详解
概念
事件委托就是把一个元素响应事件(click,keydown...)的函数委托到另一个元素。一般来讲,会把一个或一组元素的事件委托到它的父层或者更外层元素上,所以真正绑定事件的是外层元素,当时间响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件,然后在外层元素上执行函数。
事件流/事件传播
假设单击一个li标签,标签内有一个a链接
(1)DOM0:没有事件流的概念,事件不会传播
(2)IE事件流:
#1. 事件捕捉(event capturing):单击首先发生在document上,然后逐层向下依次传递给body,列表,列表项,最终到达该链接。
#2. 事件冒泡(event bubbling):单击首先发生在链接上,然后逐层向上冒泡,直到document对象。
(3)DOM2:比IE事件流多一个事件处理(target phase)
#1. 事件捕捉
#2. 事件处理
#3. 事件冒泡
原理
事件委托就是利用事件冒泡机制实现的。
事件委托的特点:
#1. 只需要将同类元素的事件委托给父级或者更外级的元素,不需要给所有元素都绑定事件,减少了内存占用
#2. 动态新增的元素不用再重新绑定事件
#3. 事件委托的实现依靠事件冒泡机制,所以不支持事件冒泡的事件不适合(Netscape只使用捕捉法)
#4. 补充上一条,focus,blur之类的时间本身没有事件冒泡机制,无法委托;mousemove,mouseout虽然有事件冒泡,但是只能不断通过位置计算定位,对于性能消耗高,也不适合事件委托
不使用事件委托
HTML:
- aaa
- bbb
- ccc
JS:
var myList=document.getElementById("myList");
var li=myList.getElementsByTagName("li");
for(var i=0;i
DOM0事件存在的问题:
#1. 给每个列表项都绑定了相同的事件,浪费了内存
#2. 当有新元素动态添加进来的时候,需要重新给元素绑定事件
使用事件委托
同样的HTML代码,不同的是JS:
var myList=document.getElementById("myList");
myList.οnclick=function(e){
var e= event||window.event;
var target=e.target ||e.srcElement;
switch(target.id){
case 'li-1':
target.style.backgroundColor='red';
break;
case 'li-2':
alert('这是第二项');
break;
case 'li-3':
target.style.color='blue';
break;
default:
alert('你完美地避开了所有选项~');
}
}
jQuery中的事件委托
$('#myList').on('click','li',function(){
alert(this.id);
});
/* //如果绑定事件的元素内部没有子元素,则e.target等同于this,都指向事件绑定的元素,否则e.target指向该元素的子元素
$('#myList').on('click','li',function(e){
alert(e.target.id);
});
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////
DOM2的事件监听器
addEventListener(事件类型[event.type],函数[handle],是否采用捕捉法[默认false,即采用冒泡机制])
attachEvent("on"+event.type , handle) (IE)
特点:
#1. 可以为一个事件指定多个监听器函数,解决事件覆盖的问题
#2. 监听器之间是相互独立的
#3. IE8及以下不支持,需要使用attachEvent(event.type , handle)
function event1(){
alert('给ul绑定成功1');
}
function event2(){
alert('给ul绑定成功2');
}
var ul=document.querySelector('#myList');
ul.addEventListener('click',event1,false);
ul.addEventListener('mouseout',event2,false);
阻断事件传播/停止事件冒泡
会有这种情况:
当我们分别给window , document , body,p均设置click的事件监听器时,如果在p上点击,就会依次弹出p,body,document,window的事件.
方法:event.stopPropagation()和event.cancelBubble(后者针对的是IE8及以下浏览器)
用法:
event.stopPropagation(); //阻止事件的进一步传播,包括冒泡和捕捉,无参数,IE8及以下不支持
event.cancelBubble=truel //true为阻止冒泡
实例:
HTML代码主要部分:
段落
JS(未阻断):
//因为要在p标签上做事件阻断,所以把函数单独拿出来
function handle(){
alert('click paragraph');
}
//注意监听器的顺序!!!
document.body.addEventListener('click',handle,false);
document.body.addEventListener('click',function(){
alert('clicked body');
},false);
document.addEventListener('click',function(){
alert('clicked document');
},false);
window.addEventListener('click',function(){
alert('clicked window');
},false);
JS(阻断,这是一种兼容性写法,推荐!):
function handle(e){
alert('click paragraph');
//阻断事件传播
e=event|| window.event;
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble=true;
}
}
防止默认行为
在浏览器模型中,有些事件自身就存在一些预定义行为。例如,单击链接会载入另一个页面。在某些情况下,我们需要禁用其默认行为。
方法:event.preventDefault()和event.returnValue=false;(IE)
实例:禁用默认行为,单击链接后,回答一个问题“are you sure you want to follow this link?”
HTML:
JS(兼容性写法,推荐!):
//这里是禁用了所有链接的默认行为
var links=document.getElementsByTagName("a");
for(var i=0;i
事件解绑/移除监听器
方法:event.removeEventListener(event.type , handle, boolean[默认false])和event.detachEvent("on"+event.type , handle)(IE)
跨浏览器的事件监听器(推荐五颗星!!)
var listenerEvent={
getEvent:function(event){
return event||window.event; //后者为IE
},
getTarget:function(event){
return event.target||event.srcElement; //后者为IE
},
stopPropagation:function(){
if(event.stopPropagation()){
event.stopPropagation();
}else{
event.cancelBubble=true; //IE
}
},
preventDefault:function(){
if(event.preventDefault()){
event.preventDefault();
}else{
event.returnValue=false; //IE
}
},
addListener:function(element,type,handle,boolean){
//第一个参数为添加监听器的对象,第二个为事件类型,第三个为函数,第四个为是否采用捕捉法(默认false,即冒泡法)
boolean=boolean||false; //设置boolean默认值为false
if(element.addEventListener){
element.addListener(type,handle,boolean);
}else if(element.attachEvent){
element.attachEvent("on"+type,handle); //IE,注意在事件类型前面加了on,如click,这里需要写成onclick
}else{
element["on"+type]=handle; //不支持以上两种写法的备用方法
}
},
removeListener:function(element,type,handle,boolean){
boolean=boolean||false; //设置boolean默认值为false
if(element.removeListener){
element.removeListener(type,handle,boolean);
}else if(element.detachEvent){
element.detachEvent("on"+type,handle); //IE,注意在事件类型前面加了on,如click,这里需要写成onclick
}else{
element["on"+type]=null;//不支持以上两种写法的备用方法
}
}
}