From dbcffb396c2db61ff96edc4162602e850797d61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Mon, 7 Dec 2020 20:28:44 +0100 Subject: [PATCH] Event: Make focus re-triggering not focus the original element back If during a focus handler another focus event is triggered: ```js elem1.on( "focus", function() { elem2.trigger( "focus" ); } ); ``` due to their synchronous nature everywhere outside of IE the hack added in gh-4279 to leverage native events causes the native `.focus()` method to be called last for the initial element, making it steal the focus back. Since the native method is already being called in `leverageNative`, we can skip that final call. This aligns with changes to the `_default` method for the `click` event that were added when `leverageNative` was introduced there. A side effect of this change is that now `focusin` will only propagate to the document for the last focused element. This is a change in behavior but it also aligns us better with how this works with native methods. Fixes gh-4382 Closes gh-4813 Ref gh-4279 --- src/event.js | 6 ++++++ test/unit/event.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/event.js b/src/event.js index 4418fbbbf..48ee8d174 100644 --- a/src/event.js +++ b/src/event.js @@ -746,6 +746,12 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp return true; }, + // Suppress native focus or blur as it's already being fired + // in leverageNative. + _default: function() { + return true; + }, + delegateType: delegateType }; } ); diff --git a/test/unit/event.js b/test/unit/event.js index 90318f8fe..edd9ecaea 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -3291,6 +3291,40 @@ QUnit.test( "native-backed events preserve trigger data (gh-1741, gh-4139)", fun }, 50 ); } ); +QUnit.test( "focus change during a focus handler (gh-4382)", function( assert ) { + assert.expect( 2 ); + + var done = assert.async(), + select = jQuery( "" ), + button = jQuery( "" ); + + jQuery( "#qunit-fixture" ) + .append( select ) + .append( button ); + + select.on( "focus", function() { + button.trigger( "focus" ); + } ); + + jQuery( document ).on( "focusin.focusTests", function( ev ) { + // Support: IE 11+ + // In IE focus is async so focusin on document is fired multiple times, + // for each of the elements. In other browsers it's fired just once, for + // the last one. + if ( ev.target === button[ 0 ] ) { + assert.ok( true, "focusin propagated to document from the button" ); + } + } ); + + select.trigger( "focus" ); + + setTimeout( function() { + assert.strictEqual( document.activeElement, button[ 0 ], "Focus redirect worked" ); + jQuery( document ).off( ".focusTests" ); + done(); + } ); +} ); + // TODO replace with an adaptation of // https://github.com/jquery/jquery/pull/1367/files#diff-a215316abbaabdf71857809e8673ea28R2464 ( function() {