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身上的。