JavaScript事件委托,或者叫事件代理,是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
借花献佛的例子(取快递):
有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。
这里其实还有2层意思的:
- 现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;
- 新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的;
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因。
如果使用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;
1. 现有同事委托代收快递
在事件冒泡的文档中,有一个简单示例,如下代码,使用的便是事件委托的思想:
<ul id="app"> <li>event_1</li> <li>event_2</li> <li>event_3</li> </ul> window.onload = function(){ var event = document.getElementById("app"); event.onclick = function(){ alert(123); } }
而当每个<li>有不同操作时,通常会将每个按钮写上id,针对不同id写出不同操作,此处不写出,而事件委托写法为:
<div id="button"> <input type="button" id="add" value="添加" /> <input type="button" id="remove" value="删除" /> <input type="button" id="move" value="移动" /> <input type="button" id="select" value="选择" /> </div> <script> window.onload = function () { var button = document.getElementById("button"); button.onclick = function (ev) { //获取事件的一般写法,兼容性的考虑 var ev = ev || window.event; //返回事件的目标节点(Event对象提供了一个属性叫target,返回事件的目标节点,这个存在兼容性,标准浏览器用ev.target,IE浏览器用event.srcElement) var target = ev.target || ev.srcElement; //nodeName返回的node名是大写字母,使用toLocaleLowerCase()转为小写 if (target.nodeName.toLocaleLowerCase() == 'input') { switch (target.id) { case 'add': alert('添加'); break; case 'remove': alert('删除'); break; case 'move': alert('移动'); break; case 'select': alert('选择'); break; } } } } </script>
2.新来的同事也需要代收快递
在新增节点时,需要给新增节点也增添事件:
<div> <input type="button" name="" id="btn" value="添加" /> <ul id="ul1"> <li>111</li> <li>222</li> <li>333</li> <li>444</li> </ul> </div> <script> //通常的写法 window.onload = function () { var oBtn = document.getElementById("btn"); var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); var num = 4; //通常写法会将操作函数包括在一起 function mHover() { //鼠标移入变红,移出变白 for (var i = 0; i < aLi.length; i++) { aLi[i].onmouseover = function () { this.style.background = 'red'; }; aLi[i].onmouseout = function () { this.style.background = '#fff'; } } } mHover(); //添加新节点 oBtn.onclick = function () { num++; var oLi = document.createElement('li'); oLi.innerHTML = 111 * num; oUl.appendChild(oLi); mHover(); }; } //事件委托写法,少了一次DOM操作 window.onload = function () { var oBtn = document.getElementById("btn"); var oUl = document.getElementById("ul1"); var aLi = oUl.getElementsByTagName('li'); var num = 4; //事件委托,添加的子元素也有事件 oUl.onmouseover = function (ev) { var ev = ev || window.event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == 'li') { target.style.background = "red"; } }; oUl.onmouseout = function (ev) { var ev = ev || window.event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == 'li') { target.style.background = "#fff"; } }; //添加新节点 oBtn.onclick = function () { num++; var oLi = document.createElement('li'); oLi.innerHTML = 111 * num; oUl.appendChild(oLi); }; } </script>