jQuery事件处理程序总是以绑定的顺序执行,这可能很烦人.例如:
$('span').click(doStuff1); $('span').click(doStuff2);
单击跨度将导致doStuff1()
触发,然后doStuff2()
.
在我绑定doStuff2()时,我想在 doStuff1()之前选择绑定它,但似乎没有任何简单的方法来执行此操作.
我想大多数人会说,只需编写如下代码:
$('span').click(function (){ doStuff2(); doStuff1(); });
但这只是一个简单的例子 - 在实践中,这样做并不总是方便.
在某些情况下,您要绑定事件,并且您绑定的对象已经有事件.在这种情况下,您可能只希望在任何其他现有事件之前触发新事件.
那么在jQuery中实现这一目标的最佳方法是什么?
更新的答案
jQuery改变了1.8中存储事件的位置.现在你知道为什么乱搞内部API这是一个坏主意:)
用于访问DOM对象事件的新内部 API可通过全局jQuery对象获得,而不是绑定到每个实例,它将DOM元素作为第一个参数,并将一个键(我们的"事件")作为第二个参数.
jQuery._data(, "events");
所以这里是jQuery 1.8的修改代码.
// [name] is the name of the event "click", "mouseover", .. // same as you'd pass it to bind() // [fn] is the handler function $.fn.bindFirst = function(name, fn) { // bind as you normally would // don't want to miss out on any jQuery magic this.on(name, fn); // Thanks to a comment by @Martin, adding support for // namespaced events too. this.each(function() { var handlers = $._data(this, 'events')[name.split('.')[0]]; // take out the handler we just inserted from the end var handler = handlers.pop(); // move it at the beginning handlers.splice(0, 0, handler); }); };
这是一个操场.
原始答案
正如@Sean发现的那样,jQuery通过元素的data
接口公开所有事件处理程序.具体element.data('events')
.使用它你总是可以编写一个简单的插件,你可以在特定的位置插入任何事件处理程序.
这是一个简单的插件,可以在列表的开头插入一个处理程序.您可以轻松扩展此项以在任何给定位置插入项目.这只是数组操作.但是由于我没有看到jQuery的源代码并且不想错过任何jQuery魔法,我通常bind
首先使用处理程序,然后重新洗牌.
// [name] is the name of the event "click", "mouseover", .. // same as you'd pass it to bind() // [fn] is the handler function $.fn.bindFirst = function(name, fn) { // bind as you normally would // don't want to miss out on any jQuery magic this.bind(name, fn); // Thanks to a comment by @Martin, adding support for // namespaced events too. var handlers = this.data('events')[name.split('.')[0]]; // take out the handler we just inserted from the end var handler = handlers.pop(); // move it at the beginning handlers.splice(0, 0, handler); };
因此,例如,对于此标记,它将起作用(例如此处):
..$("#me").click(function() { alert("1"); }); $("#me").click(function() { alert("2"); }); $("#me").bindFirst('click', function() { alert("3"); }); $("#me").click(); // alerts - 3, then 1, then 2
但是,.data('events')
据我所知,因为它不是其公共API的一部分,所以如果附加事件的基础表示从数组更改为其他内容,jQuery的更新可能会破坏您的代码.
免责声明:因为一切皆有可能:),这是你的解决方案,但我仍然会在重构现有代码方面犯错误,因为只要记住这些项目附加的顺序很快就会失控,因为你不断添加这些有序事件越来越多.
您可以执行事件的自定义命名空间.
$('span').bind('click.doStuff1',function(){doStuff1();}); $('span').bind('click.doStuff2',function(){doStuff2();});
然后,当您需要触发它们时,您可以选择订单.
$('span').trigger('click.doStuff1').trigger('click.doStuff2');
要么
$('span').trigger('click.doStuff2').trigger('click.doStuff1');
此外,只需触发click按钮,它们按照绑定的顺序触发......所以你仍然可以
$('span').trigger('click');
一个非常好的问题......我很感兴趣所以我做了一点挖掘; 对于那些感兴趣的人,这里是我去的地方,以及我想出的.
查看jQuery 1.4.2的源代码,我在第2361行和第2392行之间看到了这个块:
jQuery.each(["bind", "one"], function( i, name ) { jQuery.fn[ name ] = function( type, data, fn ) { // Handle object literals if ( typeof type === "object" ) { for ( var key in type ) { this[ name ](key, data, type[key], fn); } return this; } if ( jQuery.isFunction( data ) ) { fn = data; data = undefined; } var handler = name === "one" ? jQuery.proxy( fn, function( event ) { jQuery( this ).unbind( event, handler ); return fn.apply( this, arguments ); }) : fn; if ( type === "unload" && name !== "one" ) { this.one( type, data, fn ); } else { for ( var i = 0, l = this.length; i < l; i++ ) { jQuery.event.add( this[i], type, handler, data ); } } return this; }; });
这里有很多有趣的东西,但我们感兴趣的部分是在2384和2388行之间:
else { for ( var i = 0, l = this.length; i < l; i++ ) { jQuery.event.add( this[i], type, handler, data ); } }
每当我们打电话bind()
或者one()
我们实际打电话给jQuery.event.add()
...时,让我们来看看(1557到1672行,如果你有兴趣的话)
add: function( elem, types, handler, data ) { // ... snip ... var handleObjIn, handleObj; if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; } // ... snip ... // Init the element's event structure var elemData = jQuery.data( elem ); // ... snip ... var events = elemData.events = elemData.events || {}, eventHandle = elemData.handle, eventHandle; if ( !eventHandle ) { elemData.handle = eventHandle = function() { // Handle the second event of a trigger and when // an event is called after a page has unloaded return typeof jQuery !== "undefined" && !jQuery.event.triggered ? jQuery.event.handle.apply( eventHandle.elem, arguments ) : undefined; }; } // ... snip ... // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); types = types.split(" "); var type, i = 0, namespaces; while ( (type = types[ i++ ]) ) { handleObj = handleObjIn ? jQuery.extend({}, handleObjIn) : { handler: handler, data: data }; // Namespaced event handlers ^ | // There is is! Even marked with a nice handy comment so you couldn't miss it // (Unless of course you are not looking for it ... as I wasn't) if ( type.indexOf(".") > -1 ) { namespaces = type.split("."); type = namespaces.shift(); handleObj.namespace = namespaces.slice(0).sort().join("."); } else { namespaces = []; handleObj.namespace = ""; } handleObj.type = type; handleObj.guid = handler.guid; // Get the current list of functions bound to this event var handlers = events[ type ], special = jQuery.event.special[ type ] || {}; // Init the event handler queue if ( !handlers ) { handlers = events[ type ] = []; // ... snip ... } // ... snip ... // Add the function to the element's handler list handlers.push( handleObj ); // Keep track of which events have been used, for global triggering jQuery.event.global[ type ] = true; } // ... snip ... }
在这一点上,我意识到理解这将花费超过30分钟......所以我搜索了Stackoverflow
jquery get a list of all event handlers bound to an element
并找到了迭代绑定事件的答案:
//log them to the console (firebug, ie8) console.dir( $('#someElementId').data('events') ); //or iterate them jQuery.each($('#someElementId').data('events'), function(i, event){ jQuery.each(event, function(i, handler){ console.log( handler.toString() ); }); });
测试在Firefox中我看到每个元素events
的data
属性中的对象都有一个[some_event_name]
属性(click
在我们的例子中)附加了一个handler
对象数组,每个对象都有一个guid,一个命名空间,一个类型和一个处理程序."那么",我认为,"理论上我们应该能够以相同的方式添加对象[element].data.events.[some_event_name].push([our_handler_object);
......"
然后我去写完了我的发现......并找到很多更好的答案张贴RusselUresti ......,介绍我到新的东西,我不知道的jQuery(即使我盯着它的脸右侧.)
这证明Stackoverflow是互联网上最好的问答网站,至少在我的拙见中.
所以我发布这个是为了后人的缘故......并将其标记为社区维基,因为RussellUresti已经很好地回答了这个问题.