或叫--事件代理
参考链接或直接叫转自: 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之类的,本身就没用冒泡的特性,自然就不能用事件委托了。