jQuery .delegate() vs .live()

.live().delegate()都是藉重DOM事件會由子元素一路向父元素Bubble Up的特性,在document或特定元素統一掛上事件函數,事件被觸發時再透過選擇器比對事件目標元素是否吻合,決定要不要執行事件邏輯,藉此提升效率,同時還能將"未來才會建立的元素"也納入事件的涵蓋範圍。(細節可參考舊文)

不過,.live()與.delegate()做的事幾乎一模一樣,依江湖上的說法,1.4版推出的.delegate()又比1.3版誕生的.live()來得有效率,理由何在?

以對<table id="t">下所有<td>加掛click事件為例,多半會寫成:
$("#t td").live("click", fn); $("#t").delegate("td", "click", fn);

當執行$("#t td").live(),jQuery會監聽document的click事件,再依event.target是否吻合選擇器"#t td"決定live()所指定的click事件要不要被觸發;若使用$("#t").delegate(),jQuery監聽click事件的對象則會由document換成<table id="t">,但也一樣是比對event.target是否為td決定要不要觸發事件。二者相比,將監聽對象由document縮小到table對效能有利! 因為$("#t td").live()會過濾比對document上所有子元素產生的click事件,而$("#t").delegate("td"…)只有在table內子元素被click時才進行比對過濾,避免了非目標區元素被click時的多餘過濾流程。不過,這部分的效能損耗其實可以避免,只要改寫成$("td", document.getElementById("t")),則.live()的監聽對象就會被縮小到table而非document,此一差異性就消失了。但是,真要比效能,.live()還是難逃敗陣的命運! 為什麼?

對.live()或.delegate()來說,"td"是未來click事件觸發時比對event.target用的選擇器,理論上保留字串就夠了。當寫成$("td", document.getElementById("t")),jQuery會找尋當下所有符合的td元素,建出jQuery集合物件,但這個耗費資源建立的物件卻沒有發揮任何作用,而這個無謂的物件建立過程,在td的數量很多的情境下,就足以產生明顯效能差異。

我設計了一個實驗來驗證:

<!DOCTYPE html>
 
<html>
<head>
    <title>.delegate() test</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.4.js">  
    </script>
    <script>
        $(function () {
            $("#b1").click(function () {
                //準備記錄執行時間
                var st = new Date();
                //用.live()為#t td加掛click事件
                //注意: 以下的寫法會比$("#t td")更有效率
                $("td", document.getElementById("t"))
                .live("click", function () {
                    $("#s1").text("live - " + $(this).text());
                });
                //顯示執行時間
                alert(".live() ticks - " +
                    (new Date().getTime() - st.getTime()));
            });
            $("#b2").click(function () {
                //準備記錄執行時間
                var st = new Date();
                //先取得#t的jQuery物件,用.delegate()為td加掛click事件
                $("#t").delegate("td", "click", function () {
                    $("#s2").text("delegate - " + $(this).text());
                });
                //顯示執行時間
                alert(".delegate() ticks - " +
                    (new Date().getTime() - st.getTime()));
            });
            $("#b3").click(function () {
                //建立一個包含5萬cell的table
                var sb = [];
                var idx = 0;
                for (var r = 0; r < 2000; r++) {
                    sb.push("<tr>");
                    for (var c = 0; c < 25; c++)
                        sb.push("<td>" + (idx++) + "</td>");
                    sb.push("</tr>");
                }
                $("#t").html(sb.join('\n'));
            });
        });
    </script>
    <style>
        #t td { border: 1px solid gray; text-align: center; cursor: pointer; }
        .display span  
        {
            border: 1px solid red; display: inline-block; margin: 5px; 
            width: 100px; text-align: center; background-color: #dddddd;
        }
    </style>
</head>
<body>
<input type="button" id="b1" value="Test .live()" />
<input type="button" id="b2" value="Test .delegate()" />
<input type="button" id="b3" value="Create Table" />
<span class="display">
<span id="s1">&#160;</span>
<span id="s2">&#160;</span>
</span>
<table id="t"></table>
</body>
</html>

如上圖,Test .live()及Test .delegate()可攔載所有<td>的click事件,分別將<td>的文字內容顯示在第一列右側的兩個<span>中,而按下Create Table則會建出5萬個<td>。

第一個測試我們可以先按Test .live(),再按Test .delegate(),應該都是0 ticks完成,之後按Create Table建立<td>,點選<td>就可以驗證.live()、delegate()均成功攔截到click事件。

第二個測試,則是先按Create Table建立出5萬個<td>,再按Test .live()及Test .delegate(),在我的電腦上,按Test .delegate()仍是瞬間完成,按Test .live()卻要耗上35+ ticks,推測應就是前述建立5萬個<td>元素jQuery集合耗費的時間。

最後再補充一點,如果程式思維是針對某個元素進行一連串動作: 先針對其下的子元素掛上事件,然後再進行其他操作,.delegate()允許我們寫成$("#t").delegate("..", "click", fn).css("..", "..").attr("..", "..")的串接形式,符合jQuery的直覺寫法;而.live()由於需將子元素視為主體,就無法像.delegate()般一氣喝成,也算是.delegate()寫法的另一項優點。

參考資料:

歡迎推文分享:
Published 14 October 2011 12:15 AM 由 Jeffrey
Filed under:



意見

# pico.chang said on 18 October, 2011 03:36 AM

第一行的"藉重"應該改成"借重"吧...

# Jeffrey said on 19 October, 2011 12:05 PM

to pico.chang, 依教育部重編辭典修訂本(dict.revised.moe.edu.tw)的解釋: 借重 - 借用他人的力量,多用作請人幫忙的敬詞。儒林外史˙第二十二回:「如今要借重大爺,明日早晨把客座裡收拾乾淨了。」紅樓夢˙第六回:「若可以領我見一見更好;若不能,便借重嫂子轉致意罷了。」或作「藉重」。

我比較愛寫成"藉重"(感覺比較"雅",XD),不過"借重"是比較通俗的寫法,謝謝你的提醒。

# walter said on 20 October, 2011 07:33 AM

黑大

不好意思,問個其他問題

$("td", document.getElementById("t"))

這個種selector我在官網都找不到

不知道要去哪裡找這方法勒

謝謝

# Jeffrey said on 20 October, 2011 10:12 AM

to walter, 可參考http://api.jquery.com/jQuery/

jQuery( selector [, context] )

*selector = A string containing a selector expression

*context = A DOM Element, Document, or jQuery to use as context

在本例中,document.getElementById("t")是一個DOM element,即context參數

# Rz said on 22 October, 2011 05:47 AM

1.7之後

通通改成 on/off了

Over time, jQuery has evolved three ways to attach events to elements: .bind() , .live(), and .delegate(). Underneath it all, though, the three event APIs call the browser’s event system;

Our current event APIs aren’t going away soon, but to address the inconsistencies we’ve introduced a new and simple pair of event methods that can do the work of all three:

$(elems).on(events, selector, data, fn);

$(elems).off(events, selector, fn);

以上內容節錄至 1.7 beta1 說明

寫法不變,讚

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 

搜尋

Go

<October 2011>
SunMonTueWedThuFriSat
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
 
RSS
【工商服務】
OrcsWeb: Windows Server Hosting
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication