什么是事件委托/事件代理?
js高级程序:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
通俗点说就是利用事件的冒泡传播机制(触发当前元素的某一个行为,它父级所有元素相关行为都会被触发),如果一个容器中有很多元素都要绑定点击事件,没有必有一个一个的绑定了,只需要给最外层的容器绑定一个点击事件即可,在这个方法执行的时候,通过事件源的区分来进行不同的操作。
常用案例:
有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。
这里其实还有2层意思的:
第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;
第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。
为什么要用事件委托:
一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;
每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤,哈哈),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。
一、事件:
我们都知道在JS中,事件分三个阶段:捕获、目标、冒泡。
- 捕获:由外向内,由不具体到最具体,由document到元素
- 目标:就是事件触发的元素
- 冒泡:由内向外,由最具体到最不具体,由元素到document
二、事件处理程序:
1.DOM0级事件处理程序:
即直接在dom对象上注册时间名称,就是DOM0级写法,DOM0事件模型,所以的浏览器都支持。
先取元素,再以对象属性的形式添加事件,遵循覆盖的原则,后面绑定的事件会覆盖之前绑定的。
1)在HTML上添加
缺点:高耦合,修改不方便,html与js联系太紧密
12复制代码
<button type="button" onclick="dele()" id="btn">delete</button>
<script>
function dele(){
alert(this);//window
}
</script>123456复制代码
这样注册的事件,相当于动态调用函数(有点eval的意思),因此不会传入event对象,同时,this指向的是window,不再是触发事件的dom对象,当然如果调用时传入this则指代调用对象
<button type="button" onclick="dele(this)" id="btn">delete</button>
<script>
function dele(){
alert(this);//button
}
</script>
1234567复制代码
2)获取元素添加
var btn = document.getELementById("btn");
btn.onclick = function(){//会被后面的绑定覆盖
alert("ok")
}
btn.onclick = function(){//实际会输出'btn'
alert(this.id)
}1234567复制代码
因为事件就是在id为btn的dom节点上注册的,事件触发时,this就代表这个dom节点,可以理解为事件是被这个dom节点调用的
3)解绑DOM0级事件
原理就是最后注册的事件要覆盖之前的,最后一次注册事件设置成null,也就解除了事件绑定。
btn.onclick = null;1复制代码
2.DOM2级事件处理程序:
与DOM0级相比,DOM2级有两大优点:
1. 可以给一个元素添加多个事件处理程序,会按顺序执行,也可以添加多个事件
2. DOM2新增了捕获和冒泡的概念,使得我们可以进行事件的代理委托,为动态生成的元素绑定事件
DOM2事件通过addEventListener和removeEventListener管理,分别用于绑定事件和解除绑定
1)绑定事件:addEventListener
有三个参数:
“事件名称”: -string 事件名不用写on
“事件回调”: -function 默认传入一个event参数
“捕获/冒泡”:-boolean false为冒泡,true为捕获
这里必须要再祭出非常重要的事件过程图了:
var div = document.getElementById("div");
//冒泡事件
div.addEventListener("click", function(e){
alert("ok");
}, false);
//捕获事件
div.addEventListener("click", function(e){
alert("ok1");
}, true);123456789复制代码
2)解除绑定:removeEventListener
解除绑定同样有三个参数:
“事件名称”: -string 解除的事件名称
“事件回调”: -function 解除的回调函数,必须和注册时的一致,要想注册过的事件能够被解除,必须将回调函数保存起来,否则无法解除。
“捕获/冒泡”:-boolean 必须和注册事件时的类型一致
var div= document.getElementById("div");
//将回调存储在变量中
var fn = function(e){
alert("ok");
};
//绑定
div.addEventListener("click", fn, false);
//解除
div.removeEventListener("click", fn, false);12345678910复制代码
3.IE事件处理程序:
DOM2级绑定事件确实给我们带来了许多的便利,但是IE总是不能让我们愉快的玩耍,对于反人类的低版本IE(IE8及其以下版本浏览器),addEventListener和removeEventListene是无法使用的,必须使用IE的方法:attachEvent和detachEvent
1)绑定事件:attachEvent
有两个参数:
“事件名称”: -string 事件名必须写on
“事件回调”: -function 默认传入一个event参数
var div = document.getElementById("div");
div.attachEvent("onclick", function(e){
alert("ok");
});1234复制代码
2)解除绑定:removeEventListener
也是有两个参数:
“事件名称”: -string 事件名必须写on
“事件回调”: -function 解除的回调函数,必须和注册时的一致,要想注册过的事件能够被解除,必须将回调函数保存起来,否则无法解除。
var div= document.getElementById("div");
//将回调存储在变量中
var fn = function(e){
alert("ok");
};
//绑定
div.attachEvent("onclick", fn);
//解除
div.detachEvent("onclick", fn);12345678910复制代码
4.跨浏览器的事件处理程序:
为了浏览器的兼容,我们使用能力检测实现在各浏览器中使用事件绑定和解除事件绑定:
var eventUtil = {
//添加句柄
addHandler:function(ele,type,handler){
if(ele.addEndEventListener){
ele.addEndEventListener(type,handler,false);
}else if(ele.attachEvent){
ele.attachEvent('on'+type,handler);
}else{
ele['on'+type] = handler;
}
},
//删除句柄
removeHandler:function(ele,type,handler){
if(ele.removeEndEventListener){
ele.removeEndEventListener(type,handler,false);
}else if(ele.detachEvent){
ele.detachEvent('on'+type,handler);
}else{
ele['on'+type] = null;
}
}
}
//调用
var btn = document.getElementById('btn');
var clickHandler = function(){
alert(123)
}
eventUtil.addHandler(btn,'click',clickHandler);//绑定事件
eventUtil.removeHandler(btn,'click',clickHandler);//解除绑定1234567891011121314151617181920212223242526272829复制代码
三、事件对象
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
1.DOM中的事件对象(event)
1)type 事件类型
2)target 事件目标
3)stopPropagation() 阻止事件冒泡
4)preventDefault() 取消默认行为
2.IE中的事件对象(event||window.event)
在低版本IE(IE8-)中事件对象为window.event,所以取得事件对象的兼容写法为
target = target || window.target;1复制代码
1)type 事件类型
2)srcElement 事件目标
兼容写法:
var ele = event.target||event.srcElement;1复制代码
3)cancleBubble - boolean 阻止事件冒泡属性 true阻止;false不阻止;
4)returnValue -boolean 取消默认行为属性 true不取消;false取消;