From f68b46d7abb54cdcd3d1ce1713bc989f992d1448 Mon Sep 17 00:00:00 2001 From: jeresig Date: Tue, 9 Mar 2010 12:22:25 -0500 Subject: [PATCH] Make sure that special remove and teardown events get called when .die() is used. Additionally made sure that default actions are triggered when namespaced events are used. Fixes #6202 and #6250. --- src/event.js | 48 +++++++++++++------------------ src/manipulation.js | 2 +- test/unit/event.js | 80 ++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 91 insertions(+), 39 deletions(-) diff --git a/src/event.js b/src/event.js index b549cd5..173bab1 100644 --- a/src/event.js +++ b/src/event.js @@ -90,7 +90,9 @@ jQuery.event = { } handleObj.type = type; - handleObj.guid = handler.guid; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } // Get the current list of functions bound to this event var handlers = events[ type ], @@ -335,31 +337,31 @@ jQuery.event = { jQuery.event.trigger( event, data, parent, true ); } else if ( !event.isDefaultPrevented() ) { - var target = event.target, old, - isClick = jQuery.nodeName(target, "a") && type === "click", - special = jQuery.event.special[ type ] || {}; + var target = event.target, old, targetType = type.replace(/\..*$/, ""), + isClick = jQuery.nodeName(target, "a") && targetType === "click", + special = jQuery.event.special[ targetType ] || {}; if ( (!special._default || special._default.call( elem, event ) === false) && !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { try { - if ( target[ type ] ) { + if ( target[ targetType ] ) { // Make sure that we don't accidentally re-trigger the onFOO events - old = target[ "on" + type ]; + old = target[ "on" + targetType ]; if ( old ) { - target[ "on" + type ] = null; + target[ "on" + targetType ] = null; } jQuery.event.triggered = true; - target[ type ](); + target[ targetType ](); } // prevent IE from throwing an error for some elements with some event types, see #3533 } catch (triggerError) {} if ( old ) { - target[ "on" + type ] = old; + target[ "on" + targetType ] = old; } jQuery.event.triggered = false; @@ -381,9 +383,10 @@ jQuery.event = { event.type = namespaces.shift(); namespace_sort = namespaces.slice(0).sort(); namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)"); - event.namespace = namespace_sort.join("."); } + event.namespace = event.namespace || namespace_sort.join("."); + events = jQuery.data(this, "events"); handlers = (events || {})[ event.type ]; @@ -495,25 +498,14 @@ jQuery.event = { live: { add: function( handleObj ) { - jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); }, remove: function( handleObj ) { - var remove = true, - type = handleObj.origType.replace(rnamespaces, ""); - - jQuery.each( jQuery.data(this, "events").live || [], function() { - if ( type === this.origType.replace(rnamespaces, "") ) { - remove = false; - return false; - } - }); - - if ( remove ) { - jQuery.event.remove( this, handleObj.origType, liveHandler ); - } + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); } - }, beforeunload: { @@ -983,13 +975,13 @@ jQuery.each(["live", "die"], function( i, name ) { if ( name === "live" ) { // bind live handler for ( var j = 0, l = context.length; j < l; j++ ) { - jQuery.event.add( context[j], liveConvert( type, selector ), + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); } } else { // unbind live handler - context.unbind( liveConvert( type, selector ), fn ); + context.unbind( "live." + liveConvert( type, selector ), fn ); } } @@ -1077,7 +1069,7 @@ function liveHandler( event ) { } function liveConvert( type, selector ) { - return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&"); + return (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&"); } jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + diff --git a/src/manipulation.js b/src/manipulation.js index 270c7bc..be12aa1 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -561,7 +561,7 @@ jQuery.extend({ if ( id ) { data = cache[ id ]; - if ( data.events ) { + if ( data && data.events ) { for ( var type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); diff --git a/test/unit/event.js b/test/unit/event.js index 786a46e..f800fd5 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -978,17 +978,20 @@ test(".live()/.die()", function() { jQuery("#nothiddendiv").trigger("click"); equals( called, 1, "Verify that only one click occurred." ); + called = 0; jQuery("#anchor2").trigger("click"); - equals( called, 2, "Verify that only one click occurred." ); + equals( called, 1, "Verify that only one click occurred." ); // Make sure that only one callback is removed jQuery("#anchor2").die("click", callback); + called = 0; jQuery("#nothiddendiv").trigger("click"); - equals( called, 3, "Verify that only one click occurred." ); + equals( called, 1, "Verify that only one click occurred." ); + called = 0; jQuery("#anchor2").trigger("click"); - equals( called, 3, "Verify that no click occurred." ); + equals( called, 0, "Verify that no click occurred." ); // Make sure that it still works if the selector is the same, // but the event type is different @@ -997,11 +1000,13 @@ test(".live()/.die()", function() { // Cleanup jQuery("#nothiddendiv").die("click", callback); + called = 0; jQuery("#nothiddendiv").trigger("click"); - equals( called, 3, "Verify that no click occurred." ); + equals( called, 0, "Verify that no click occurred." ); + called = 0; jQuery("#nothiddendiv").trigger("foo"); - equals( called, 4, "Verify that one foo occurred." ); + equals( called, 1, "Verify that one foo occurred." ); // Cleanup jQuery("#nothiddendiv").die("foo", callback); @@ -1307,6 +1312,56 @@ test("live with submit", function() { jQuery("body").die("submit"); }); +test("live with special events", function() { + expect(13); + + jQuery.event.special.foo = { + setup: function( data, namespaces, handler ) { + ok( true, "Setup run." ); + }, + teardown: function( namespaces ) { + ok( true, "Teardown run." ); + }, + add: function( handleObj ) { + ok( true, "Add run." ); + }, + remove: function( handleObj ) { + ok( true, "Remove run." ); + }, + _default: function( event ) { + ok( true, "Default run." ); + } + }; + + // Run: setup, add + jQuery("#liveSpan1").live("foo.a", function(e){ + ok( true, "Handler 1 run." ); + }); + + // Run: add + jQuery("#liveSpan1").live("foo.b", function(e){ + ok( true, "Handler 2 run." ); + }); + + // Run: Handler 1, Handler 2, Default + jQuery("#liveSpan1").trigger("foo"); + + // Run: Handler 1, Default + // TODO: Namespace doesn't trigger default (?) + jQuery("#liveSpan1").trigger("foo.a"); + + // Run: remove + jQuery("#liveSpan1").die("foo.a"); + + // Run: Handler 2, Default + jQuery("#liveSpan1").trigger("foo"); + + // Run: remove, teardown + jQuery("#liveSpan1").die("foo"); + + delete jQuery.event.special.foo; +}); + test(".delegate()/.undelegate()", function() { expect(65); @@ -1460,17 +1515,20 @@ test(".delegate()/.undelegate()", function() { jQuery("#nothiddendiv").trigger("click"); equals( called, 1, "Verify that only one click occurred." ); + called = 0; jQuery("#anchor2").trigger("click"); - equals( called, 2, "Verify that only one click occurred." ); + equals( called, 1, "Verify that only one click occurred." ); // Make sure that only one callback is removed jQuery("#body").undelegate("#anchor2", "click", callback); + called = 0; jQuery("#nothiddendiv").trigger("click"); - equals( called, 3, "Verify that only one click occurred." ); + equals( called, 1, "Verify that only one click occurred." ); + called = 0; jQuery("#anchor2").trigger("click"); - equals( called, 3, "Verify that no click occurred." ); + equals( called, 0, "Verify that no click occurred." ); // Make sure that it still works if the selector is the same, // but the event type is different @@ -1479,11 +1537,13 @@ test(".delegate()/.undelegate()", function() { // Cleanup jQuery("#body").undelegate("#nothiddendiv", "click", callback); + called = 0; jQuery("#nothiddendiv").trigger("click"); - equals( called, 3, "Verify that no click occurred." ); + equals( called, 0, "Verify that no click occurred." ); + called = 0; jQuery("#nothiddendiv").trigger("foo"); - equals( called, 4, "Verify that one foo occurred." ); + equals( called, 1, "Verify that one foo occurred." ); // Cleanup jQuery("#body").undelegate("#nothiddendiv", "foo", callback); -- 1.7.10.4