Switch from recursion to iteration for event triggering. Move event-name namespace processing out of event handler, since it has to always go through trigger.

This commit is contained in:
Dave Methvin 2011-02-10 23:43:45 -05:00
parent e0856738e6
commit bfa32317d2

View file

@ -277,13 +277,12 @@ jQuery.event = {
} }
}, },
// bubbling is internal trigger: function( event, data, elem ) {
trigger: function( event, data, elem /*, bubbling */ ) {
// Event object or event type // Event object or event type
var type = event.type || event, var type = event.type || event,
bubbling = arguments[3]; bubbling = arguments[3],
namespaces = [];
if ( !bubbling ) {
event = typeof event === "object" ? event = typeof event === "object" ?
// jQuery.Event object // jQuery.Event object
event[ jQuery.expando ] ? event : event[ jQuery.expando ] ? event :
@ -293,10 +292,19 @@ jQuery.event = {
jQuery.Event(type); jQuery.Event(type);
if ( type.indexOf("!") >= 0 ) { if ( type.indexOf("!") >= 0 ) {
// Exclusive events trigger only for the bare event type (no namespaces)
event.type = type = type.slice(0, -1); event.type = type = type.slice(0, -1);
event.exclusive = true; event.exclusive = true;
} }
if ( type.indexOf(".") >= 0 ) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split(".");
event.type = type = namespaces.shift();
namespaces.sort();
}
event.namespace = namespaces.join(".");
event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
// Handle a global trigger // Handle a global trigger
if ( !elem ) { if ( !elem ) {
// Don't bubble custom events when global (to avoid too much overhead) // Don't bubble custom events when global (to avoid too much overhead)
@ -317,13 +325,12 @@ jQuery.event = {
} }
}); });
} }
return;
} }
// Handle triggering a single element
// don't do events on text and comment nodes // don't do events on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return undefined; return;
} }
// Clean up in case it is reused // Clean up in case it is reused
@ -333,62 +340,53 @@ jQuery.event = {
// Clone the incoming data, if any // Clone the incoming data, if any
data = jQuery.makeArray( data ); data = jQuery.makeArray( data );
data.unshift( event ); data.unshift( event );
}
event.currentTarget = elem; // Always fire on the current element, e.g. triggerHandler or global trigger
var cur = elem;
// Trigger the event, it is assumed that "handle" is a function do {
var handle = jQuery._data( elem, "handle" ); var handle = jQuery._data( cur, "handle" );
event.currentTarget = cur;
if ( handle ) { if ( handle ) {
handle.apply( elem, data ); handle.apply( cur, data );
} }
var parent = elem.parentNode || elem.ownerDocument; // Trigger an inline bound script; IE dies on special-char event names
try {
// Trigger an inline bound script if ( jQuery.acceptData( cur ) && cur[ "on" + type ] && cur[ "on" + type ].apply( cur, data ) === false ) {
try {
if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
event.result = false; event.result = false;
event.preventDefault(); event.preventDefault();
} }
} } catch ( ieError ) {}
// prevent IE from throwing an error for some elements with some event types, see #3533 cur = cur.parentNode || cur.ownerDocument;
} catch (inlineError) {} } while ( cur && !event.isPropagationStopped() );
if ( !event.isPropagationStopped() && parent ) { // If nobody prevented the default action, do it now
jQuery.event.trigger( event, data, parent, true ); if ( !event.isDefaultPrevented() ) {
} else if ( !event.isDefaultPrevented() ) {
var old, var old,
target = event.target, special = jQuery.event.special[ type ] || {};
targetType = type.replace( rnamespaces, "" ),
isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
special = jQuery.event.special[ targetType ] || {};
if ( (!special._default || special._default.call( elem, event ) === false) && if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
// IE may die here on non-alpha event names or hidden elements (#3533)
try { try {
if ( target[ targetType ] ) { if ( elem[ type ] ) {
// Make sure that we don't accidentally re-trigger the onFOO events // Don't accidentally re-trigger the onFOO events
old = target[ "on" + targetType ]; old = elem[ "on" + type ];
if ( old ) { if ( old ) {
target[ "on" + targetType ] = null; elem[ "on" + type ] = null;
} }
jQuery.event.triggered = event.type; jQuery.event.triggered = event.type;
target[ targetType ](); elem[ type ]();
} }
} catch ( ieError ) {}
// prevent IE from throwing an error for some elements with some event types, see #3533
} catch (triggerError) {}
if ( old ) { if ( old ) {
target[ "on" + targetType ] = old; elem[ "on" + type ] = old;
} }
jQuery.event.triggered = undefined; jQuery.event.triggered = undefined;
@ -397,38 +395,24 @@ jQuery.event = {
}, },
handle: function( event ) { handle: function( event ) {
var all, handlers, namespaces, namespace_re, events, // It's rare to arrive without handlers to call, so do all setup here.
namespace_sort = [], // Copy the handler list since a called handler may add/remove events.
event = jQuery.event.fix( event || window.event );
var events = jQuery._data( this, "events" ),
handlers = ((events || {})[ event.type ] || []).slice(0),
all_handlers = !event.exclusive && !event.namespace,
args = jQuery.makeArray( arguments ); args = jQuery.makeArray( arguments );
event = args[0] = jQuery.event.fix( event || window.event ); // Use the fix-ed Event rather than the (read-only) native event
args[0] = event;
event.currentTarget = this; event.currentTarget = this;
// Namespaced event handlers
all = event.type.indexOf(".") < 0 && !event.exclusive;
if ( !all ) {
namespaces = event.type.split(".");
event.type = namespaces.shift();
namespace_sort = namespaces.slice(0).sort();
namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
}
event.namespace = event.namespace || namespace_sort.join(".");
events = jQuery._data(this, "events");
handlers = (events || {})[ event.type ];
if ( events && handlers ) {
// Clone the handlers to prevent manipulation
handlers = handlers.slice(0);
for ( var j = 0, l = handlers.length; j < l; j++ ) { for ( var j = 0, l = handlers.length; j < l; j++ ) {
var handleObj = handlers[ j ]; var handleObj = handlers[ j ];
// Filter the functions by class // Triggered event must 1) be non-exclusive and have no namespace, or
if ( all || namespace_re.test( handleObj.namespace ) ) { // 2) have namespace(s) a subset or equal to those in the bound event.
if ( all_handlers || event.namespace_re.test( handleObj.namespace ) ) {
// Pass in a reference to the handler function itself // Pass in a reference to the handler function itself
// So that we can later remove it // So that we can later remove it
event.handler = handleObj.handler; event.handler = handleObj.handler;
@ -450,8 +434,6 @@ jQuery.event = {
} }
} }
} }
}
return event.result; return event.result;
}, },