或叫--事件代理
参考链接或直接叫转自: js中的事件委托或是事件代理详解
1、定义
事件委托:事件委托利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事。
‘事件代理’就是把原本需要绑定的事件委托给父元素,让父元素负责事件监听。
事件委托就是利用事件冒泡原理,将子元素要绑定的事件委托给父元素,让父元素负责事件监听,指定父元素为事件处理程序,那么父元素就可以管理某一类型的所有事件。
网上的经典例子:取快递
有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。
注意!这里其实还有2层意思的:
第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;
第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。
2、为什么要用事件委托
如果有很多同样的标签要使用同样的事件,如果用for循环则性能很差。访问dom的次数越多,引起浏览器重绘和重排的次数也就越多,延长整个交互事件。
但如果使用事件委托,就会将所有的操作放到js程序中,与dom的操作只需要交互一次,提高性能。
且如果给100个li绑定click事件,每个函数都是一个对象,会占用内存。如果写100个click函数,占用内存。如果用事件委托,那么只用对父级元素进行操作,只需要一个内存空间。
3、事件委托的原理
利用事件的冒泡原理。
例如:有一个dom节点树:div>ul>li>a,如果给最外层的div加点击事件,那么里面的ul,li,a做点击事件时,都会冒泡到最外层,所以都会触发,这就是事件委托,委托他们的父级代为执行事件。
4、事件委托的实现
第一:看不用事件委托的代码,对每一个li创建了一个click事件,性能差
<ul id="ulll">
<li>111</li>
<li>222</li>
<li>333</li>
<li>4444</li>
</ul>
var oui = document.getElementById("ulll");
var ali = oui.getElementsByTagName("li");
for (var i = 0; i < ali.length; i++) {
ali[i].onclick = function () {
console.log(this.innerText)
}
}第二:看使用了事件委托
// event对象提供target属性,可以返回事件的目标节点,即
//target可以表示为当前的事件操作的dom,但不是真正操作dom
//标准浏览器用ev.target,ie浏览器用event.srcElement
var oui = document.getElementById("ulll");
oui.onclick = function (ev) {
var ev = ev || window.event;
var target = ev.target || window.srcElement;
if (target.nodeName.toLowerCase() == 'li') {
console.log(target.innerText);
}
}第三:,上面是li是同一个方法,如果li不是同一个方法,使用普通方法:
var add=document.getElementById("add");
var remove=document.getElementById("remove");
var move=document.getElementById("move");
var select=document.getElementById("select");
add.onclick=function(){
console.log(this.value);
}
remove.onclick=function(){
console.log(this.value);
}
move.onclick=function(){
console.log(this.value);
}
select.onclick=function(){
console.log(this.value);
}
第***委托的方法:
var obox = document.getElementsByClassName("box")[0];
obox.onclick = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == 'input') {
switch (target.id) {
case 'add':
// 不能用this
console.log(target.value);
break;
case 'remove':
console.log(target.value);
break;
case 'move':
console.log(target.value);
break;
case 'select':
console.log(target.value);
break;
}
}
}第五:有新增的节点咋办呢?新增的节点怎么给它添加事件呢?
先看正常的添加事件
var btn = document.getElementById("btn");
var oui = document.getElementById("ulll");
var ali = document.getElementsByTagName("li");
function mHover(){
console.log(ali);
for(var i=0;i<ali.length;i++){
ali[i].onmouseover=function(){
this.style.backgroundColor='red';
};
ali[i].onmouseout=function(){
this.style.backgroundColor='yellow';
}
}
}
var num=4;
mHover();
btn.onclick=function(){
num++;
var oli=document.createElement('li');
oli.innerHTML=111*num;
oui.appendChild(oli);
mHover();
}第六:事件委托方法:
var num = 4;
oui.onmouseover = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == 'li') {
target.style.backgroundColor = 'red';
}
}
oui.onmouseout = function (ev) {
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == 'li') {
target.style.background = "yellow";
}
};
//添加新节点
btn.onclick = function () {
num++;
var oli = document.createElement('li');
oli.innerHTML = 111 * num;
oui.appendChild(oli);
};第七:
<ul id="test">
<li>
<p>11111111111</p>
</li>
<li>
<div>
22222222
</div>
</li>
<li>
<span>3333333333</span>
</li>
<li>4444444</li>
</ul>
// 现在给一个场景 ul > li > div > p,div占满li,
// p占满div,还是给ul绑定时间,
// 需要判断点击的是不是li(假设li里面的结构是不固定的)
// ,那么e.target就可能是p,也有可能是div,
// 这种情况你会怎么处理呢?
// 点击到里面的事件时,直接指定到li上
var oui=document.getElementById('test');
oui.addEventListener('click',function(ev){
var target=ev.target;
while(target!=oui){
if(target.tagName.toLowerCase()=='li'){
console.log('li click');
}
target=target.parentNode;
}
})适合用事件委托的事件:click、mousedown,mouseup,keydown,keyup,keypress
不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,
focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

京公网安备 11010502036488号