]> source.dussan.org Git - jquery.git/commitdiff
Event: Make focus re-triggering not focus the original element back
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Mon, 7 Dec 2020 19:28:44 +0000 (20:28 +0100)
committerGitHub <noreply@github.com>
Mon, 7 Dec 2020 19:28:44 +0000 (20:28 +0100)
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
test/unit/event.js

index 4418fbbbf809eeab906938eec3ec77f5fae4e085..48ee8d174b39bf0ee17d4b88d97457662855bc11 100644 (file)
@@ -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
        };
 } );
index 90318f8fefb3c8ad7884374802aa2de7cda27157..edd9ecaead8a8796e2ce8bb2a60f5aa25a3577ac 100644 (file)
@@ -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( "<select><option selected='selected'>A</option></select>" ),
+               button = jQuery( "<button>Focus target</button>" );
+
+       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() {