From 26b0e913dda386b6b2848196689a02a2da6aa40d Mon Sep 17 00:00:00 2001 From: jeresig Date: Fri, 26 Feb 2010 09:26:14 -0500 Subject: [PATCH] Make sure that live events bubble unless explicitly told not to, like a normal event. Fixes #6182. --- src/event.js | 28 +++++++++++++++++-------- src/traversing.js | 6 +++--- test/unit/event.js | 58 ++++++++++++++++++++++++++++++---------------------- 3 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/event.js b/src/event.js index d91231e..db08bc0 100644 --- a/src/event.js +++ b/src/event.js @@ -984,8 +984,8 @@ jQuery.each(["live", "die"], function( i, name ) { }); function liveHandler( event ) { - var stop, elems = [], selectors = [], args = arguments, - related, match, handleObj, elem, j, i, l, data, + var stop, maxLevel, elems = [], selectors = [], + related, match, handleObj, elem, j, i, l, data, close, events = jQuery.data( this, "events" ); // Make sure we avoid non-left-click bubbling in Firefox (#3861) @@ -1011,11 +1011,13 @@ function liveHandler( event ) { match = jQuery( event.target ).closest( selectors, event.currentTarget ); for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + for ( j = 0; j < live.length; j++ ) { handleObj = live[j]; - if ( match[i].selector === handleObj.selector ) { - elem = match[i].elem; + if ( close.selector === handleObj.selector ) { + elem = close.elem; related = null; // Those two events require additional checking @@ -1024,7 +1026,7 @@ function liveHandler( event ) { } if ( !related || related !== elem ) { - elems.push({ elem: elem, handleObj: handleObj }); + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); } } } @@ -1032,13 +1034,23 @@ function liveHandler( event ) { for ( i = 0, l = elems.length; i < l; i++ ) { match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + event.currentTarget = match.elem; event.data = match.handleObj.data; event.handleObj = match.handleObj; - if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) { - stop = false; - break; + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } } } diff --git a/src/traversing.js b/src/traversing.js index a99dfdf..6ae9924 100644 --- a/src/traversing.js +++ b/src/traversing.js @@ -82,7 +82,7 @@ jQuery.fn.extend({ closest: function( selectors, context ) { if ( jQuery.isArray( selectors ) ) { - var ret = [], cur = this[0], match, matches = {}, selector; + var ret = [], cur = this[0], match, matches = {}, selector, level = 1; if ( cur && selectors.length ) { for ( var i = 0, l = selectors.length; i < l; i++ ) { @@ -100,11 +100,11 @@ jQuery.fn.extend({ match = matches[selector]; if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { - ret.push({ selector: selector, elem: cur }); - delete matches[selector]; + ret.push({ selector: selector, elem: cur, level: level }); } } cur = cur.parentNode; + level++; } } diff --git a/test/unit/event.js b/test/unit/event.js index 7d66c9c..cbde90e 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -799,7 +799,7 @@ test(".live()/.die()", function() { submit = 0, div = 0, livea = 0, liveb = 0; jQuery("div#nothiddendivchild").trigger("click"); equals( submit, 0, "Click on inner div" ); - equals( div, 1, "Click on inner div" ); + equals( div, 2, "Click on inner div" ); equals( livea, 1, "Click on inner div" ); equals( liveb, 1, "Click on inner div" ); @@ -815,7 +815,7 @@ test(".live()/.die()", function() { submit = 0, div = 0, livea = 0, liveb = 0; jQuery("div#nothiddendivchild").trigger("click"); equals( submit, 0, "die Click on inner div" ); - equals( div, 1, "die Click on inner div" ); + equals( div, 2, "die Click on inner div" ); equals( livea, 1, "die Click on inner div" ); equals( liveb, 1, "die Click on inner div" ); @@ -824,7 +824,7 @@ test(".live()/.die()", function() { jQuery("div#nothiddendivchild").die("click"); jQuery("div#nothiddendivchild").trigger("click"); equals( submit, 0, "die Click on inner div" ); - equals( div, 1, "die Click on inner div" ); + equals( div, 2, "die Click on inner div" ); equals( livea, 1, "die Click on inner div" ); equals( liveb, 0, "die Click on inner div" ); @@ -842,7 +842,7 @@ test(".live()/.die()", function() { jQuery("div#nothiddendivchild").trigger("click"); equals( submit, 0, "stopPropagation Click on inner div" ); equals( div, 1, "stopPropagation Click on inner div" ); - equals( livea, 1, "stopPropagation Click on inner div" ); + equals( livea, 0, "stopPropagation Click on inner div" ); equals( liveb, 1, "stopPropagation Click on inner div" ); // Make sure click events only fire with primary click @@ -1252,6 +1252,7 @@ test(".delegate()/.undelegate()", function() { equals( liveb, 0, "Click on body" ); // This should trigger two events + submit = 0, div = 0, livea = 0, liveb = 0; jQuery("div#nothiddendiv").trigger("click"); equals( submit, 0, "Click on div" ); equals( div, 1, "Click on div" ); @@ -1259,55 +1260,62 @@ test(".delegate()/.undelegate()", function() { equals( liveb, 0, "Click on div" ); // This should trigger three events (w/ bubbling) + submit = 0, div = 0, livea = 0, liveb = 0; jQuery("div#nothiddendivchild").trigger("click"); equals( submit, 0, "Click on inner div" ); equals( div, 2, "Click on inner div" ); - equals( livea, 2, "Click on inner div" ); + equals( livea, 1, "Click on inner div" ); equals( liveb, 1, "Click on inner div" ); // This should trigger one submit + submit = 0, div = 0, livea = 0, liveb = 0; jQuery("div#nothiddendivchild").trigger("submit"); equals( submit, 1, "Submit on div" ); - equals( div, 2, "Submit on div" ); - equals( livea, 2, "Submit on div" ); - equals( liveb, 1, "Submit on div" ); + equals( div, 0, "Submit on div" ); + equals( livea, 0, "Submit on div" ); + equals( liveb, 0, "Submit on div" ); // Make sure no other events were removed in the process + submit = 0, div = 0, livea = 0, liveb = 0; jQuery("div#nothiddendivchild").trigger("click"); - equals( submit, 1, "undelegate Click on inner div" ); - equals( div, 3, "undelegate Click on inner div" ); - equals( livea, 3, "undelegate Click on inner div" ); - equals( liveb, 2, "undelegate Click on inner div" ); + equals( submit, 0, "undelegate Click on inner div" ); + equals( div, 2, "undelegate Click on inner div" ); + equals( livea, 1, "undelegate Click on inner div" ); + equals( liveb, 1, "undelegate Click on inner div" ); // Now make sure that the removal works + submit = 0, div = 0, livea = 0, liveb = 0; jQuery("#body").undelegate("div#nothiddendivchild", "click"); jQuery("div#nothiddendivchild").trigger("click"); - equals( submit, 1, "undelegate Click on inner div" ); - equals( div, 4, "undelegate Click on inner div" ); - equals( livea, 4, "undelegate Click on inner div" ); - equals( liveb, 2, "undelegate Click on inner div" ); + equals( submit, 0, "undelegate Click on inner div" ); + equals( div, 2, "undelegate Click on inner div" ); + equals( livea, 1, "undelegate Click on inner div" ); + equals( liveb, 0, "undelegate Click on inner div" ); // Make sure that the click wasn't removed too early + submit = 0, div = 0, livea = 0, liveb = 0; jQuery("div#nothiddendiv").trigger("click"); - equals( submit, 1, "undelegate Click on inner div" ); - equals( div, 5, "undelegate Click on inner div" ); - equals( livea, 5, "undelegate Click on inner div" ); - equals( liveb, 2, "undelegate Click on inner div" ); + equals( submit, 0, "undelegate Click on inner div" ); + equals( div, 1, "undelegate Click on inner div" ); + equals( livea, 1, "undelegate Click on inner div" ); + equals( liveb, 0, "undelegate Click on inner div" ); // Make sure that stopPropgation doesn't stop live events + submit = 0, div = 0, livea = 0, liveb = 0; jQuery("#body").delegate("div#nothiddendivchild", "click", function(e){ liveb++; e.stopPropagation(); }); jQuery("div#nothiddendivchild").trigger("click"); - equals( submit, 1, "stopPropagation Click on inner div" ); - equals( div, 6, "stopPropagation Click on inner div" ); - equals( livea, 6, "stopPropagation Click on inner div" ); - equals( liveb, 3, "stopPropagation Click on inner div" ); + equals( submit, 0, "stopPropagation Click on inner div" ); + equals( div, 1, "stopPropagation Click on inner div" ); + equals( livea, 0, "stopPropagation Click on inner div" ); + equals( liveb, 1, "stopPropagation Click on inner div" ); // Make sure click events only fire with primary click + submit = 0, div = 0, livea = 0, liveb = 0; var event = jQuery.Event("click"); event.button = 1; jQuery("div#nothiddendiv").trigger(event); - equals( livea, 6, "delegate secondary click" ); + equals( livea, 0, "delegate secondary click" ); jQuery("#body").undelegate("div#nothiddendivchild", "click"); jQuery("#body").undelegate("div#nothiddendiv", "click"); -- 1.7.10.4