Board logo

標題: jQuery 教學 - delegate 事件委派 (v1.4.2 以上適用) [打印本頁]

作者: wmh    時間: 2010-4-8 02:14     標題: jQuery 教學 - delegate 事件委派 (v1.4.2 以上適用)

這次要跟大家介紹的是 jQuery 1.4.2 新增的 delegate (事件委派),適合用在一次要綁定大量元素的事件時,可以減少記憶體的耗費並提昇綁定的效率。

[jsg.example]<h2><font color="slategray" size="5"><a id="範例1" name="範例1">[範例1]</a></font></h2>[/jsg.example]
首先請用滑鼠在以下方格上移動,看看這樣的效果:
[jsg.example]
<style type="text/css">
#test-delegate {
  width: auto;
  border-collapse: separate;
}
#test-delegate td {
    font-size: 0;
    height: 10px;
    width: 10px;
    background-color: silver;
    border-right: 1px solid gray;
    border-bottom: 1px solid gray;
}
</style>
<table cellspacing="1" id="test-delegate">
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
    <td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
</table>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$j = jQuery.noConflict();
$j(function() {
    $j("#test-delegate").delegate("td", "hover", function () {
        $j(this).animate({opacity: 0.25}, 300, function () {
            $j(this).animate({opacity: 1}, 300);
        });
    });
});
</script>
[/jsg.example]
上面的方格是一個大的 table,裡面每個 td 都被綁定了 hover 的事件,當滑鼠移過去時先漸漸增加透明度至 25%,之後再逐漸恢復。

如果是你會怎麼來處理呢?這樣嗎:
//方法 1
...
<td onmouseover="dosomething();"></td>
<td onmouseover="dosomething();"></td>
<td onmouseover="dosomething();"></td>
...
上面的方式當然是遜斃了,熟悉 jQuery 的人至少會用批次的方式來處理,像是:
//方法 2:找 #mytable 底下所有的 td,全部綁定 hover 事件
$("#mytable td").hover(function () {
    $(this).animate({opacity: 0.25}, 300, function () {
        $(this).animate({opacity: 1}, 300);
    });
});
方法 2 當然比方法 1 高明了一點,至少 code 維持了 jQuery 的風格,相當精簡。但是這樣的做法是讓每一個 td 都綁定了一個事件,缺點有兩個:


基於以上的原因,推薦你改用 delegate,綁定速度快,省記憶體,寫法只要稍稍改變一下,請看:
//方法 3:針對 #mytable 綁定 hover 事件,但是只對底下的 td 有效
$("#mytable").delegate("td", "hover", function () {
    $(this).animate({opacity: 0.25}, 300, function () {
        $(this).animate({opacity: 1}, 300);
    });
});
請直接對照方法 2 和方法 3 的程式碼第一行,寫法差異不大,但是效果真的差很多。如果一次有大量的元素需要綁定,建議改用 delegate 的語法。

此外,delegate 還有跟 live 一樣的效用,對於未來產生的新元素也是馬上讓綁定事件生效,以上面的例子,如果我在 table 動態增加兩行,新增的那兩行的所有 td 也會馬上有相同的動畫效果,相當不錯吧。


[jsg.example]<h2><font color="slategray" size="5"><a id="範例2" name="範例2">[範例2]</a></font></h2>[/jsg.example]
再來一個簡單的範例,介紹針對不同元素進行不同的處理。先看看 HTML 的部份:
<ul id="links">
    <li><a href="http://jsgears.com/">http://jsgears.com/</a></li>
    <li><a href="http://www.google.com/">http://www.google.com/</a></li>
    <li><a href="http://www.yahoo.com/">http://www.yahoo.com/</a></li>
</ul>
上面是幾個超連結的列表,沒有帶 target 的屬性,如果我要透過 JavaScript 來幫我達到點擊後另開視窗,同樣可以用 delegate 的方式,將事件綁定在 #link 並針對下面所有的 <a>,在事件處理的函式中,則針對個別 <a> 的 href 屬性去另開視窗。JavaScript 的部份如下:
$("#link").delegate("a", "click", function () {
    window.open($(this).attr("href"));
    return false;
});
跟一般事件處理函式一樣,在裡面使用 $(this) 表示被觸發事件的元素,透過讀取該元素的特定屬性,就可以針對不同元素進行不同的處理。

[jsg.example]<h2><font color="slategray" size="5"><a id="Ending" name="Ending">[Ending]</a></font></h2>[/jsg.example]
希望以上兩個範例夠清楚,能讓大家明白所謂「事件委派」的奧妙,我只需要把事件綁定在「一個」比較上層的元素,然後針對底下「多個」元素進行事件的處理,寫法和原本的事件綁定並沒有差異很大,但是使用得當的情況下,卻可以省下 client 端 browser 的許多資源。很不錯吧,趕快學起來,有機會用得上的~
作者: JS領事    時間: 2010-4-8 19:47

好神奇……

原理是什麼?
作者: wmh    時間: 2010-4-8 23:16

我想應該是綁定在上層元素的事件,會去判斷事件發生的原始點 (透過 e.target, e.srcElement),比對如果是符合要處理的元素,就進行處理。

實做的方法可以直接去看 jQuery 的原始碼。
作者: rayin    時間: 2011-3-29 09:57

受教了, 謝謝版大
作者: lolmuta    時間: 2015-11-22 09:32

我個人想法,我並沒有看原始碼…功力還不夠,

所有子元素的click事件會上傳到父元素,這條路是本來就已經鋪上了。


當按下按鈕時,javascript會去找所有您建立的click event,是否適用於該按鈕,並將該按鈕的事件往上傳

當按下按鈕時,
如果是一個一個建立的,
javascript要去找click event的數量就會多了一些,所以會比較慢。
如果是用delegate來建的,
javascript要去找click event的數量就少一些, 然後click事件上傳,父元素有被delegate,就看看是不是我們設定的元素,若是就觸發,可以說完全沒有做什麼尋找的動作。

雖然有種似懂非懂的感覺,最終還是得看原始碼才能了解。




歡迎光臨 jsGears.com 技術論壇 - AJAX, JavaScript, jQuery, 網站開發, 前端效能優化 (http://jsgears.com/)