Commit f68b46d7 authored by jeresig's avatar jeresig

Make sure that special remove and teardown events get called when .die() is...

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.
parent 2fbe3fbf
...@@ -90,7 +90,9 @@ jQuery.event = { ...@@ -90,7 +90,9 @@ jQuery.event = {
} }
handleObj.type = type; handleObj.type = type;
handleObj.guid = handler.guid; if ( !handleObj.guid ) {
handleObj.guid = handler.guid;
}
// Get the current list of functions bound to this event // Get the current list of functions bound to this event
var handlers = events[ type ], var handlers = events[ type ],
...@@ -335,31 +337,31 @@ jQuery.event = { ...@@ -335,31 +337,31 @@ jQuery.event = {
jQuery.event.trigger( event, data, parent, true ); jQuery.event.trigger( event, data, parent, true );
} else if ( !event.isDefaultPrevented() ) { } else if ( !event.isDefaultPrevented() ) {
var target = event.target, old, var target = event.target, old, targetType = type.replace(/\..*$/, ""),
isClick = jQuery.nodeName(target, "a") && type === "click", isClick = jQuery.nodeName(target, "a") && targetType === "click",
special = jQuery.event.special[ type ] || {}; special = jQuery.event.special[ targetType ] || {};
if ( (!special._default || special._default.call( elem, event ) === false) && if ( (!special._default || special._default.call( elem, event ) === false) &&
!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
try { try {
if ( target[ type ] ) { if ( target[ targetType ] ) {
// Make sure that we don't accidentally re-trigger the onFOO events // Make sure that we don't accidentally re-trigger the onFOO events
old = target[ "on" + type ]; old = target[ "on" + targetType ];
if ( old ) { if ( old ) {
target[ "on" + type ] = null; target[ "on" + targetType ] = null;
} }
jQuery.event.triggered = true; jQuery.event.triggered = true;
target[ type ](); target[ targetType ]();
} }
// prevent IE from throwing an error for some elements with some event types, see #3533 // prevent IE from throwing an error for some elements with some event types, see #3533
} catch (triggerError) {} } catch (triggerError) {}
if ( old ) { if ( old ) {
target[ "on" + type ] = old; target[ "on" + targetType ] = old;
} }
jQuery.event.triggered = false; jQuery.event.triggered = false;
...@@ -381,9 +383,10 @@ jQuery.event = { ...@@ -381,9 +383,10 @@ jQuery.event = {
event.type = namespaces.shift(); event.type = namespaces.shift();
namespace_sort = namespaces.slice(0).sort(); namespace_sort = namespaces.slice(0).sort();
namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)"); namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
event.namespace = namespace_sort.join(".");
} }
event.namespace = event.namespace || namespace_sort.join(".");
events = jQuery.data(this, "events"); events = jQuery.data(this, "events");
handlers = (events || {})[ event.type ]; handlers = (events || {})[ event.type ];
...@@ -495,25 +498,14 @@ jQuery.event = { ...@@ -495,25 +498,14 @@ jQuery.event = {
live: { live: {
add: function( handleObj ) { 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 ) { remove: function( handleObj ) {
var remove = true, jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
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 );
}
} }
}, },
beforeunload: { beforeunload: {
...@@ -983,13 +975,13 @@ jQuery.each(["live", "die"], function( i, name ) { ...@@ -983,13 +975,13 @@ jQuery.each(["live", "die"], function( i, name ) {
if ( name === "live" ) { if ( name === "live" ) {
// bind live handler // bind live handler
for ( var j = 0, l = context.length; j < l; j++ ) { 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 } ); { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
} }
} else { } else {
// unbind live handler // unbind live handler
context.unbind( liveConvert( type, selector ), fn ); context.unbind( "live." + liveConvert( type, selector ), fn );
} }
} }
...@@ -1077,7 +1069,7 @@ function liveHandler( event ) { ...@@ -1077,7 +1069,7 @@ function liveHandler( event ) {
} }
function liveConvert( type, selector ) { 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 " + jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
......
...@@ -561,7 +561,7 @@ jQuery.extend({ ...@@ -561,7 +561,7 @@ jQuery.extend({
if ( id ) { if ( id ) {
data = cache[ id ]; data = cache[ id ];
if ( data.events ) { if ( data && data.events ) {
for ( var type in data.events ) { for ( var type in data.events ) {
if ( special[ type ] ) { if ( special[ type ] ) {
jQuery.event.remove( elem, type ); jQuery.event.remove( elem, type );
......
...@@ -978,17 +978,20 @@ test(".live()/.die()", function() { ...@@ -978,17 +978,20 @@ test(".live()/.die()", function() {
jQuery("#nothiddendiv").trigger("click"); jQuery("#nothiddendiv").trigger("click");
equals( called, 1, "Verify that only one click occurred." ); equals( called, 1, "Verify that only one click occurred." );
called = 0;
jQuery("#anchor2").trigger("click"); 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 // Make sure that only one callback is removed
jQuery("#anchor2").die("click", callback); jQuery("#anchor2").die("click", callback);
called = 0;
jQuery("#nothiddendiv").trigger("click"); 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"); 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, // Make sure that it still works if the selector is the same,
// but the event type is different // but the event type is different
...@@ -997,11 +1000,13 @@ test(".live()/.die()", function() { ...@@ -997,11 +1000,13 @@ test(".live()/.die()", function() {
// Cleanup // Cleanup
jQuery("#nothiddendiv").die("click", callback); jQuery("#nothiddendiv").die("click", callback);
called = 0;
jQuery("#nothiddendiv").trigger("click"); 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"); jQuery("#nothiddendiv").trigger("foo");
equals( called, 4, "Verify that one foo occurred." ); equals( called, 1, "Verify that one foo occurred." );
// Cleanup // Cleanup
jQuery("#nothiddendiv").die("foo", callback); jQuery("#nothiddendiv").die("foo", callback);
...@@ -1307,6 +1312,56 @@ test("live with submit", function() { ...@@ -1307,6 +1312,56 @@ test("live with submit", function() {
jQuery("body").die("submit"); 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() { test(".delegate()/.undelegate()", function() {
expect(65); expect(65);
...@@ -1460,17 +1515,20 @@ test(".delegate()/.undelegate()", function() { ...@@ -1460,17 +1515,20 @@ test(".delegate()/.undelegate()", function() {
jQuery("#nothiddendiv").trigger("click"); jQuery("#nothiddendiv").trigger("click");
equals( called, 1, "Verify that only one click occurred." ); equals( called, 1, "Verify that only one click occurred." );
called = 0;
jQuery("#anchor2").trigger("click"); 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 // Make sure that only one callback is removed
jQuery("#body").undelegate("#anchor2", "click", callback); jQuery("#body").undelegate("#anchor2", "click", callback);
called = 0;
jQuery("#nothiddendiv").trigger("click"); 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"); 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, // Make sure that it still works if the selector is the same,
// but the event type is different // but the event type is different
...@@ -1479,11 +1537,13 @@ test(".delegate()/.undelegate()", function() { ...@@ -1479,11 +1537,13 @@ test(".delegate()/.undelegate()", function() {
// Cleanup // Cleanup
jQuery("#body").undelegate("#nothiddendiv", "click", callback); jQuery("#body").undelegate("#nothiddendiv", "click", callback);
called = 0;
jQuery("#nothiddendiv").trigger("click"); 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"); jQuery("#nothiddendiv").trigger("foo");
equals( called, 4, "Verify that one foo occurred." ); equals( called, 1, "Verify that one foo occurred." );
// Cleanup // Cleanup
jQuery("#body").undelegate("#nothiddendiv", "foo", callback); jQuery("#body").undelegate("#nothiddendiv", "foo", callback);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment