diff --git a/src/event.js b/src/event.js index b63b93b96..48d9095c9 100644 --- a/src/event.js +++ b/src/event.js @@ -554,9 +554,13 @@ function leverageNative( el, type, expectSync ) { if ( ( event.isTrigger & 1 ) && this[ type ] ) { // Interrupt processing of the outer synthetic .trigger()ed event - if ( !saved ) { + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. saved = slice.call( arguments ); dataPriv.set( this, type, saved ); @@ -569,14 +573,14 @@ function leverageNative( el, type, expectSync ) { if ( saved !== result || notAsync ) { dataPriv.set( this, type, false ); } else { - result = undefined; + result = {}; } if ( saved !== result ) { // Cancel the outer synthetic event event.stopImmediatePropagation(); event.preventDefault(); - return result; + return result.value; } // If this is an inner synthetic event for an event with a bubbling surrogate @@ -591,17 +595,19 @@ function leverageNative( el, type, expectSync ) { // If this is a native event triggered above, everything is now in order // Fire an inner synthetic event with the original arguments - } else if ( saved ) { + } else if ( saved.length ) { // ...and capture the result - dataPriv.set( this, type, jQuery.event.trigger( + dataPriv.set( this, type, { + value: jQuery.event.trigger( - // Support: IE <=9 - 11+ - // Extend with the prototype to reset the above stopImmediatePropagation() - jQuery.extend( saved.shift(), jQuery.Event.prototype ), - saved, - this - ) ); + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); // Abort handling of the native event event.stopImmediatePropagation(); diff --git a/test/unit/event.js b/test/unit/event.js index c7497b9b0..17706a8b1 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -3041,6 +3041,49 @@ QUnit.test( "focus-blur order (#12868)", function( assert ) { }, 50 ); } ); +QUnit.test( "Event handling works with multiple async focus events (gh-4350)", function( assert ) { + assert.expect( 3 ); + + var remaining = 3, + input = jQuery( "#name" ), + + // Support: IE <=9 - 11+ + // focus and blur events are asynchronous; this is the resulting mess. + // The browser window must be topmost for this to work properly!! + done = assert.async(); + + input + .on( "focus", function() { + remaining--; + assert.ok( true, "received focus event, expecting " + remaining + " more" ); + if ( remaining > 0 ) { + input.trigger( "blur" ); + } else { + done(); + } + } ) + .on( "blur", function() { + setTimeout( function() { + input.trigger( "focus" ); + } ); + } ); + + // gain focus + input.trigger( "focus" ); + + // DOM focus is unreliable in TestSwarm + setTimeout( function() { + if ( QUnit.isSwarm && remaining === 3 ) { + assert.ok( true, "GAP: Could not observe focus change" ); + assert.ok( true, "GAP: Could not observe focus change" ); + assert.ok( true, "GAP: Could not observe focus change" ); + setTimeout( function() { + done(); + } ); + } + } ); +} ); + QUnit.test( "native-backed events preserve trigger data (gh-1741, gh-4139)", function( assert ) { assert.expect( 17 );