JS中的事件委托(事件代理)
一步一步来说说事件委托(或者有的资料叫事件代理)
- js中事件冒泡我们知道,子元素身上的事件会冒泡到父元素身上。
- 事件代理就是,本来应该加在子元素身上的事件,我们却把事件加在了其父级身上。
- 那就产生了问题:父级那么多子元素,怎么区分事件本应该是哪个子元素的?
- 答案是:event对象里记录的有“事件源”,它就是发生事件的子元素。
- 它存在兼容性问题,在老的IE下,事件源是 window.event.srcElement,其他浏览器是 event.target
- 用事件委托有什么好处呢?
- 第一个好处是效率高,比如,不用for循环为子元素添加事件了
- 第二个好处是,js新生成的子元素也不用新为其添加事件了,程序逻辑上比较方便
--------------------
好吧,下面还是用例子来说,更容易理解。
---------------------
例子1. 页面有个ul包含着4个li,鼠标移动到li上,li背景变成红色,移出,背景恢复原色。
如果按照以前的写法,代码如下:
1 <ul id="ul1"> 2 <li>111</li> 3 <li>222</li> 4 <li>333</li> 5 <li>444</li> 6 </ul> 7 8 <script type="text/javascript"> 9 window.onload = function(){ 10 var oUl = document.getElementById('ul1'); 11 var aLi = oUl.children; 12 console.log(aLi); 13 14 //传统方法,li身上添加事件,需要用for循环,找到每个li 15 for (var i=0;i<aLi.length;i++) { 16 aLi[i].onmouseover = function() { 17 this.style.background = 'red'; 18 } 19 aLi[i].onmouseout = function(){ 20 this.style.background = ''; 21 } 22 }//for结束 23 24 25 } 26 </script>
现在用事件委托的方式,onmouseover、onmouseout方法要加在ul身上了,再通过找事件源的方式,改变li背景,代码如下:
上面ul的html代码不变,js部分变为
1 <script type="text/javascript"> 2 window.onload = function(){ 3 var oUl = document.getElementById('ul1'); 4 oUl.onmouseover = function(ev){ 5 var ev = ev || window.event; 6 var oLi = ev.srcElement || ev.target; 7 oLi.style.background = 'red'; 8 } 9 10 oUl.onmouseout = function(ev){ 11 var ev = ev || window.event; 12 var oLi = ev.srcElement || ev.target; 13 oLi.style.background = ''; 14 } 15 16 } 17 </script>
效果如下:
但是会发现,鼠标移到了ul身上而不是某个li身上时,获取的事件源是ul,那么整个ul背景将变红,这不是想要的结果,怎么办?
答曰:加个判断。通过事件源的nodeName判断是不是li,是的话,才做出反应,不是的话,不理它。为了防止nodeName在不同浏览器获取的字母大小写不同,加个toLowerCase()
所以,上面的js代码更改如下:
1 <script type="text/javascript"> 2 window.onload = function(){ 3 var oUl = document.getElementById('ul1'); 4 5 oUl.onmouseover = function(ev){ 6 var ev = ev || window.event; 7 var oLi = ev.srcElement || ev.target; 8 if(oLi.nodeName.toLowerCase() == 'li'){ 9 oLi.style.background = 'red'; 10 } 11 12 } 13 14 oUl.onmouseout = function(ev){ 15 var ev = ev || window.event; 16 var oLi = ev.srcElement || ev.target; 17 if(oLi.nodeName.toLowerCase() == 'li'){ 18 oLi.style.background = ''; 19 } 20 } 21 22 23 } 24 </script> 25
效果如下很完美:
这就是不用for循环写一堆了,下面再来说说第二个好处:js新生成的子元素也不用新为其添加事件了,程序逻辑上比较方便
上面的文件,假如我再新添加个按钮,点击按钮,ul里就新增加个li,如果用传统的方法,for循环为li添加事件,问题就出现了,最开始有的4个li是有onmouseover和onmouseout事件的,但是后来动态生成的li里没有这两个事件处理函数,因为for循环的时候它还没生成。怎么办呢?只能在按钮点击后,生成li,然后再为生成的li再绑定事件,真是麻烦的很。而事件委托的方式就没事,当后来动态生成的li出现的时候,不用做改变,移到它身上,还是变色的,因为事件是绑定在ul身上的。
新浪微博里,当你发一条新微博出去,发出去的信息在页面上显示,鼠标移动到新发的信息的人头像上时,依然会有很多事件,如果用原来的方式,就要做很多处理,事件委托的话,就很方便了!