在我的另外一篇文章 解析Javascript事件冒泡機制里的最后,從冒泡機制的角度談了一點對Javascript 事件委托的理解。現(xiàn)在單獨把事件委托 拿出來和大家探討一下。
什么是委托?
所謂的委托,現(xiàn)實意義上講是指將自己的事務囑托他人代為處理。比如說甲委托乙去做某些事兒,那么,甲則是委托人,乙是被委托人。真正做事情的是乙,即被委托人,而委托人甲 則是把相應的信息傳遞給被委托人乙,自己本該做的事情交給了乙來做,如下圖這個例子:
那么在我們javascript 里,什么是事件委托呢?
事件委托
事件委托 允許我們不必為某些特定的節(jié)點添加事件監(jiān)聽器,而是將事件監(jiān)聽器添加到(這些節(jié)點的)某個 parent節(jié)點上。事件監(jiān)聽器分析冒泡事件,去找到匹配的子節(jié)點元素,然后做出相應的事件響應。
事件委托具體是怎么工作的呢? 我們從下面的簡單的例子開始,給大家展示事件委托的工作原理。
先看下面一段代碼:
- <div id="box1" class="box1">
- <p id="para1">Paragraph 1</p>
- <p id="para2">Paragraph 2</p>
- <p id="para3">Paragraph 3</p>
- <p id="para4">Paragraph 4</p>
- <p id="para5">Paragraph 5</p>
- </div>
我現(xiàn)在想每點擊一次div1 下的 p 元素時,彈出 "paraX is clicked." 的提示框,為此,我們一般的做法就是給每一個p元素增加 相應的事件監(jiān)聽:
- <script type="text/javascript">
- window.onload = function() {
- //添加事件監(jiān)聽
- document.getElementById("para1").addEventListener("click",eventPerformed);
- document.getElementById("para2").addEventListener("click",eventPerformed);
- document.getElementById("para3").addEventListener("click",eventPerformed);
- document.getElementById("para4").addEventListener("click",eventPerformed);
- document.getElementById("para5").addEventListener("click",eventPerformed);
- }
- //事件相應函數(shù)
- function eventPerformed(event){
- alert(event.target.id+" is clicked.");
- }
- </script>
是的,上述的代碼完全可以實現(xiàn)上的功能。我們思考這個問題,如果這個div1 下有非常多個 p元素,我們總不能每一個都寫上形如document.getElementById("paraX").addEventListener("click",eventPerformed);的代碼片段吧,這樣的話就太機械了!另外,如果在實際的應用中,很有可能同過js在 div1下動態(tài)生成p 元素,這時候,我們相應地還要添加事件處理函數(shù),像這種動態(tài)添加的動作很有可能分散在我們 應用的很多個角落,這樣動態(tài)添加處理函數(shù)將是一個非常蛋疼的事兒!
解決方法: 利用 事件冒泡傳遞的機制,將本來本元素要完成的事件處理邏輯,委托給 父類節(jié)點,父類節(jié)點根據(jù)觸發(fā)事件的節(jié)點信息,執(zhí)行對應的事件處理邏輯。
- <script type="text/javascript">
- window.onload = function() {
- //添加事件監(jiān)聽
- document.getElementById("box1").addEventListener("click",eventPerformed);
- }
- //事件相應函數(shù)
- function eventPerformed(event){
- if(event.target.nodeName == 'P')
- {
- alert(event.target.id+" is clicked.");
- }
- }
- </script>
通過上述的代碼,就可以輕松搞定P 元素的事件處理函數(shù)綁定了,動態(tài)添加元素的時候,不需要再手動地綁定處理函數(shù)了。
可以看出,使用事件委托,可以簡化了事件的處理邏輯,避免了多余的事件處理函數(shù),進而節(jié)約了一定的內(nèi)存。
但是,事件委托也是有缺點的: 如果現(xiàn)在的dom 元素分為很多很多層,對于底層事件的委托,有可能在事件冒泡的過程中,中途被某個節(jié)點 終止冒泡了,這樣事件就傳遞不到上層,則委托就會失敗了。
jquery 對事件委托的支持
在jQuery里對事件委托的支持,有以下幾個函數(shù):
其中,live() 和die()、delegate()和undelegate() 是成對出現(xiàn)的,分別是綁定和解綁的過程。
delegate(selector,[type],[data],fn)
jquery 的delegate 方法是典型的為事件委托準備的。
看一下相應的定義:
delegate(selector,[type],[data],fn)
selector:選擇器字符串,用于過濾器觸發(fā)事件的元素。
type: 附加到元素的一個或多個事件。由空格分隔多個事件值。必須是有效的事件。
data: 傳遞到函數(shù)的額外數(shù)據(jù)
fn: 當事件發(fā)生時運行的函數(shù)
概述:
指定的元素(屬于被選元素的子元素)添加一個或多個事件處理程序,并規(guī)定當這些事件發(fā)生時運行的函數(shù)。
使用 delegate() 方法的事件處理程序適用于當前或未來的元素(比如由腳本創(chuàng)建的新元素)。
例如,我們將上面的例子用jquery實現(xiàn):
- <script type="text/javascript">
- $(function() {
- //讓box1 處理來自 子元素P的click事件委托
- $("#box1").delegate("p", "click", function(event) {
- alert(event.target.id + " is clicked.");
- });
- })
- </script>
上述的代碼實現(xiàn)了對box1 的子元素P 的click事件的委托處理。如果我們在js中動態(tài)地給box1 增加子元素P,相應的處理函數(shù)也會對其有效。
undelegate([selector,[type],fn])
相應地,如果想取消對應的事件委托,可以使用以下代碼:
- $("#box1").undelegate("p","click");
delegate的使用方式是對某個元素的子元素進行事件委托處理,即形如:$("parentElement").delegate("siblings","eventType",function); parentElement元素作為一個冒泡事件處理的被委托者。jQuery還有另外一種方式:將元素的事件處理委托給DOM根節(jié)點來處理,這種方式是live()方式:live(type, [data], fn)
type : 事件類型
data :附加的額外數(shù)據(jù)
fn : 相應的處理函數(shù)
描述:jQuery 給所有匹配的元素附加一個事件處理函數(shù),即使這個元素是以后再添加進來的也有效。
備注: 自jQuery1.9后 就廢除這個函數(shù),只能在jQuery1.9以前的版本中使用。
將上述的功能用此方法實現(xiàn):
- <script type="text/javascript">
- $(function() {
- $("p").live("click",function(event) {
- alert(event.target.id + " is clicked.");
- });
- })
- </script>
die(type, [fn])
備注: 自jQuery1.9后 就廢除這個函數(shù),只能在jQuery1.9以前的版本中使用。
從元素中刪除先前用.live()綁定的所有事件.(此方法與live正好完全相反。)如果不帶參數(shù),則所有綁定的live事件都會被移除。
與live() 相對應,取消綁定,則用下列代碼:
在事件綁定上,jQuery 提供了一種更通用的函數(shù):
on(events,[selector],[data],fn)
參數(shù):
events:一個或多個用空格分隔的事件類型和可選的命名空間,如"click"或"keydown.myPlugin" 。
selector:一個選擇器字符串用于過濾器的觸發(fā)事件的選擇器元素的后代。如果選擇的< null或省略,當它到達選定的元素,事件總是觸發(fā)。
data:當一個事件被觸發(fā)時要傳遞event.data給事件處理函數(shù)。
fn:該事件被觸發(fā)時執(zhí)行的函數(shù)。 false 值也可以做一個函數(shù)的簡寫,返回false。
將上述的功能改用on函數(shù)實現(xiàn):
- <script type="text/javascript">
- $(function() {
- $("#box1").on("click","p",function(event) {
- alert(event.target.id + " is clicked.");
- });
- })
- </script>
off(events,[selector],[fn])
在選擇元素上移除一個或多個事件的事件處理函數(shù)。off() 方法移除用.on()綁定的事件處理程序。
移除上面on 綁定的委托:
- $("#box1").off("click","p");
在網(wǎng)上看到了關(guān)于 事件委托的總結(jié),感覺挺不錯的,就翻譯一下貼在這里,跟大家分享一下,如有錯誤或紕漏,請指出。 點擊我查看原文;
總結(jié):
先決條件是要有一個容器,允許通用的事件處理函數(shù)。
算法:
- 將事件處理函數(shù)綁定到容器上,
在事件處理函數(shù)內(nèi)獲取 event.target,- 根據(jù)不同的event.target作相應的處理
應用場景:
- 需要為子元素用一個事件處理函數(shù) 處理相同的動作;
- 簡化不同動作間的結(jié)構(gòu)
優(yōu)點:
- 簡化了初始化的過程,減少了多余的事件處理函數(shù),進而節(jié)省了內(nèi)存。
- 簡化了dom節(jié)點更新時,相應事件的更新
- Allows to use
innerHTML
without additional processing.
缺點:
- 第一,要求事件在IE中必須冒泡. 大多數(shù)的事件會冒泡,但是并不是所有的。對于其他的瀏覽器而言,捕獲階段也會同樣適用。
- 第二,理論上委托會導致瀏覽器額外的加載,因為在容器內(nèi)的任意一個地方事件的發(fā)生,都會運行事件處理函數(shù),所以多數(shù)情況下事件處理函數(shù)都是在空循環(huán)(沒有意義的動作),通常不是什么大不了的事兒。