其实我只是想做一个可以恶搞 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 就不会被执行了,这一点可得小心为上。