Skip to content

Commit 9aa0c69

Browse files
committed
Fixed bubbling of live events (if an inner element handles an event first - and stops progatation - then the parent event doesn't encounter the event). Thanks to Irae for the patch. Fixes bug jquery#3980.
1 parent 0ae7802 commit 9aa0c69

File tree

5 files changed

+43
-7
lines changed

5 files changed

+43
-7
lines changed

src/core.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,14 +346,18 @@ jQuery.fn = jQuery.prototype = {
346346
},
347347

348348
closest: function( selector ) {
349-
var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null;
349+
var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
350+
closer = 0;
350351

351352
return this.map(function(){
352353
var cur = this;
353354
while ( cur && cur.ownerDocument ) {
354-
if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) )
355+
if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
356+
jQuery.data(cur, "closest", closer);
355357
return cur;
358+
}
356359
cur = cur.parentNode;
360+
closer++;
357361
}
358362
});
359363
},

src/event.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,9 +571,13 @@ function liveHandler( event ){
571571
}
572572
});
573573

574+
elems.sort(function(a,b) {
575+
return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
576+
});
577+
574578
jQuery.each(elems, function(){
575579
if ( this.fn.call(this.elem, event, this.fn.data) === false )
576-
stop = false;
580+
return (stop = false);
577581
});
578582

579583
return stop;

test/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ <h2 id="userAgent"></h2>
213213
<span>...</span><a id="linkWithNoHrefWithTabIndex" tabindex="1">Eat some funyuns</a><span>...</span>
214214
<span>...</span><a id="linkWithNoHrefWithNegativeTabIndex" tabindex="-1">Eat some funyuns</a><span>...</span>
215215
</div>
216+
217+
<div id="liveHandlerOrder">
218+
<span id="liveSpan1"><a href="#" id="liveLink1"></a></span>
219+
<span id="liveSpan2"><a href="#" id="liveLink2"></a></span>
220+
</div>
216221
</div>
217222
</dl>
218223

test/unit/event.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ test("toggle(Function, Function, ...)", function() {
474474
});
475475

476476
test(".live()/.die()", function() {
477-
expect(42);
477+
expect(46);
478478

479479
var submit = 0, div = 0, livea = 0, liveb = 0;
480480

@@ -611,6 +611,29 @@ test(".live()/.die()", function() {
611611

612612
// Cleanup
613613
jQuery("#nothiddendivchild").die("click");
614+
615+
// Verify that .live() ocurs and cancel buble in the same order as
616+
// we would expect .bind() and .click() without delegation
617+
var lived = 0, livee = 0;
618+
619+
// bind one pair in one order
620+
jQuery('span#liveSpan1 a').live('click', function(){ lived++; return false; });
621+
jQuery('span#liveSpan1').live('click', function(){ livee++; });
622+
623+
jQuery('span#liveSpan1 a').click();
624+
equals( lived, 1, "Verify that only one first handler occurred." );
625+
equals( livee, 0, "Verify that second handler don't." );
626+
627+
// and one pair in inverse
628+
jQuery('#liveHandlerOrder span#liveSpan2').live('click', function(){ livee++; });
629+
jQuery('#liveHandlerOrder span#liveSpan2 a').live('click', function(){ lived++; return false; });
630+
631+
jQuery('span#liveSpan2 a').click();
632+
equals( lived, 2, "Verify that only one first handler occurred." );
633+
equals( livee, 0, "Verify that second handler don't." );
634+
635+
// Cleanup
636+
jQuery("span#liveSpan1 a, span#liveSpan1, span#liveSpan2 a, span#liveSpan2").die("click");
614637
});
615638

616639
/*

test/unit/selector.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ test("child and adjacent", function() {
189189
reset();
190190

191191
t( "Last Child", "p:last-child", ["sap"] );
192-
t( "Last Child", "a:last-child", ["simon1","anchor1","mark","yahoo","anchor2","simon"] );
192+
t( "Last Child", "a:last-child", ["simon1","anchor1","mark","yahoo","anchor2","simon","liveLink1","liveLink2"] );
193193

194194
t( "Nth-child", "#main form#form > *:nth-child(2)", ["text1"] );
195195
t( "Nth-child", "#main form#form > :nth-child(2)", ["text1"] );
@@ -278,7 +278,7 @@ test("pseudo (:) selectors", function() {
278278
expect(53);
279279
t( "First Child", "p:first-child", ["firstp","sndp"] );
280280
t( "Last Child", "p:last-child", ["sap"] );
281-
t( "Only Child", "a:only-child", ["simon1","anchor1","yahoo","anchor2"] );
281+
t( "Only Child", "a:only-child", ["simon1","anchor1","yahoo","anchor2","liveLink1","liveLink2"] );
282282
t( "Empty", "ul:empty", ["firstUL"] );
283283
t( "Enabled UI Element", "#form input:not([type=hidden]):enabled", ["text1","radio1","radio2","check1","check2","hidden2","name"] );
284284
t( "Disabled UI Element", "#form input:disabled", ["text2"] );
@@ -290,7 +290,7 @@ test("pseudo (:) selectors", function() {
290290
t( "Text Contains", "a:contains('Google Groups (Link)')", ["groups"] );
291291
t( "Text Contains", "a:contains('(Link)')", ["groups"] );
292292

293-
t( "Element Preceded By", "p ~ div", ["foo","fx-queue","fx-tests", "moretests","tabindex-tests"] );
293+
t( "Element Preceded By", "p ~ div", ["foo","fx-queue","fx-tests", "moretests","tabindex-tests", "liveHandlerOrder"] );
294294
t( "Not", "a.blog:not(.link)", ["mark"] );
295295
t( "Not - multiple", "#form option:not(:contains('Nothing'),#option1b,:selected)", ["option1c", "option1d", "option2b", "option2c", "option3d", "option3e"] );
296296
//t( "Not - complex", "#form option:not([id^='opt']:nth-child(-n+3))", [ "option1a", "option1d", "option2d", "option3d", "option3e"] );

0 commit comments

Comments
 (0)