iT邦幫忙

5

[jQuery note.] 關於 bind, live 與 evnets 的筆記與 Plugin

其實我只是想做一個可以惡搞 Events 執行順序的 plugin 而已(攤手)。
噗浪討論串 1
噗浪討論串 2

Plugin 先奉上,

(function($) {
    $.fn.superbind = function(order, type, data, fn) {
        if ( $.isFunction( data ) ) {
            fn = data;
            data = undefined;
        }
         
        var order = (typeof order !== "number" || order < 0) ? 0 : order;
         
        return this.each(function() {
            /* TODO: fire event before inline event.
            if ( order === 0 && typeof $(this).attr("on"+type) === "function") {
            }
            */
            var _ = setTimeout( function() {
                var guid = 0;
                $.each( $.cache, function() {
                    if ( this.events && this.events[ "live" ] && this.events[ type ] ) {
                        var len = this.events[ type ].length - 1;
                        guid = parseInt(this.events[ "live" ][ len ].guid) + 1;
                    }
                });
                $.each( $.cache, function() {
                    var reorder = [], events_len = 0, reorder_len = 0;
                    var obj = {
                        data: data,
                        guid: guid,
                        handler: fn,
                        namespace: "",
                        type: type
                    };
                    if ( this.events && this.events[ type ] && !this.events[ "live" ] ) {
                        events_len = this.events[ type ].length;
                        order = (order > events_len) ? events_len : order;
                         
                        for(var i in this.events[ type ]) {
                            if (order === events_len && parseInt(i) === events_len-1) {
                                reorder.push(this.events[ type ][i]);
                                reorder.push(obj);
                                order = -1;
                            } else if ( parseInt(i) === order || order === 0) {
                                reorder.push(obj);
                                reorder.push(this.events[ type ][i]);
                                order = -1;
                            } else {
                                reorder.push(this.events[ type ][i]);
                            }
                        }
                        this.events[ type ] = reorder;
                        reorder = [];
                        delete reorder;
                    }
                });
            }, 20);
             
            return this;
        });
    };
})(jQuery);

最近為了一些應用上的效果,所以特別研究了一下 Events 的資料。挖開 jQuery 之後才發現,這是一個很奇妙的世界(疑)。結合各家神人討論,所以我來心得報告一下(喂)。特別感謝大澤木小鐵費拉諾蘭大公鼎力相助(一拜)。

通常在 jQuery 裡面,我們使用 Events 的作法,有三種:

// 1. 使用 bind 來綁定 event
$("#elem").bind("click", function(e) { ... });
// 2. 直接使用 event
$("#elem").click(function(e) { ... });
// 3. 使用 live 來綁定 event
$("#elem").live("click", function(e) { ... });

其實在 DOM 中還有一種,則是:

<!-- 4. inline 的寫法 -->
<div id="elem" onclick=" ... "> ... </div>

總共有四種方法,我們可以使用 Events。然而,這四種使用 Event 的優先順序,分別是:

4 > 3 = 2 > 1

疑?不要問我為什麼,因為實驗的結果就是這樣(喂)。當我們把 jQuery 三種使用 Events 的資料傾印出來的時候,會發現一件很神奇的事情,使用 bind/click 直接綁定的 Events,其資料是儲存於元件本身,而使用 live 綁定時,資料會儲存於 document 裡面。再根據費拉諾蘭大公所述,三者的資料都會儲存於 $.cache 裡面(使用陣列儲存)。

所以,我們分別來看這些事情:

<!doctype html>
<html lang="en" class="no-js">

    <meta charset="utf-8">
    <style>
    #main {
      width: 400px;
      height: 600px;
      border: 1px solid #000000;
      color: #ff3333;
    }
  </style>


     
    <div id="container">
        <div id="main" onclick="console.log(4);"></div>
    </div>
 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script>!window.jQuery && document.write('<script src="./js/jquery-1.4.2.min.js"><\/script>')</script>
    <script>
    $(document).ready(function() {
        $("#main").bind("click", function(e) {
            console.log(3);
            console.log($.data(this).events);
        });
        $("#main").click(function(e) {
            console.log(2);
            console.log($.data(this).events);
        });
        $("#main").live("click", function(e) {
            console.log(1);
            console.log($.data(this).events);
            console.log($.data(document).events);
            console.log("$.cache");
            console.log($.cache);
        });
    });
    </script>

以上的例子請使用 Chrome 或是 Firefox with Firebug 開啟,資訊會記錄在 console 底下。

依照 $.cache 的儲存方法,他會將元素上的 click/bind 跟 live 分開存放,但是請注意,他並不會儲存 DOM 裡 inline 寫法的 onclick,所以,並不能單純使用這個方法去判斷是否有該 Events 存在或是執行。此外,因為 $.cache 是以陣列的方式儲存這些事情,而且他也並不能直覺的取得所綁定的對象,所以在資訊取出上,有相對麻煩的地方。

當你將 $.cache 列出來時,你會發現 live 這個方法,會產生一個相同的 event 方法,換句話說,你使用 bind/click 去綁定一個 Event 時,他只會有一個 event 方法,而,倘若你使用 live 去綁定一個 Event 時,他同時會衍生出一個該 Event 的 event 方法(超饒舌)。

// 這一段 Javascript 可以加在上述 live 的 function 底下
 
console.log("======================");
for(i in $.cache) {
    console.log("這是第 "+i.toString()+" 個 Event cache。");
    var events = $.cache[i].events || null;
    if(typeof events === "object") {
        for(event in events) {
            console.log("這是使用 "+event.toString()+" 綁定。");
            console.log(events[event]);
            if(events[event].length > 1) {
                for(j in events[event]) {
                    console.log("這是第 "+(parseInt(j)+1).toString()+" 個 Event 綁定。");
                    console.log(events[event][j]);
                }
            }
        }
    }
}

上述例子,點了方框之後,他會將 $.cache 的資訊列出來給你看。你會發現 live 會多一份 Event 的綁定,至於為什麼?因為他是 live 啊(喂)!

這裡有一個不小心實驗出來的問題,當你使用 bind/click 時,倘若在 handler function 中使用了 return false; 的話,那麼 live 所綁定的 Event 會略過不執行。原因是,因為 live 把 Event 偷偷地綁在 document 上面,由於 DOM 中 addEventListener 的規則,你使用 return false 就等同於 stopPropagation() 的意思(其實 return false 也會連帶執行 preventDefault())。

所以,在 document 的子元件執行 stopPropagation(),想當然爾,document 所綁定的 Events 就不會被執行了,這一點可得小心為上。


尚未有邦友留言

立即登入留言