]> source.dussan.org Git - jquery.git/commitdiff
Event: Use only one focusin/out handler per matching window & document
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Mon, 6 Apr 2020 18:34:40 +0000 (20:34 +0200)
committerGitHub <noreply@github.com>
Mon, 6 Apr 2020 18:34:40 +0000 (20:34 +0200)
The `doc` variable in:
https://github.com/jquery/jquery/blob/3.4.1/src/event/focusin.js#L30
matched `document` for `document` & `window` for `window`, creating two
separate wrapper event handlers & calling handlers twice if at least one
`focusout` or `focusin` handler was attached on *both* `window` & `document`,
or on `window` & another regular node.

Also, fix the "focusin from an iframe" test to actually verify the behavior
from commit 1cecf64e5aa415367a7dae0b55c2dd17b591442d - the commit that
introduced the regression - to make sure we don't regress on either front.

Fixes gh-4652
Closes gh-4656

src/event/focusin.js
test/unit/event.js

index 7faef298134558ccaf2170b753036ef082340c66..3da86c7871d80c5f48032a9d11e119d2c31df2c8 100644 (file)
@@ -27,7 +27,10 @@ if ( !support.focusin ) {
 
                jQuery.event.special[ fix ] = {
                        setup: function() {
-                               var doc = this.ownerDocument || this,
+
+                               // Handle: regular nodes (via `this.ownerDocument`), window
+                               // (via `this.document`) & document (via `this`).
+                               var doc = this.ownerDocument || this.document || this,
                                        attaches = dataPriv.access( doc, fix );
 
                                if ( !attaches ) {
@@ -36,7 +39,7 @@ if ( !support.focusin ) {
                                dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
                        },
                        teardown: function() {
-                               var doc = this.ownerDocument || this,
+                               var doc = this.ownerDocument || this.document || this,
                                        attaches = dataPriv.access( doc, fix ) - 1;
 
                                if ( !attaches ) {
index ea9b67f16fceef02c6135ccd4d89d8dc4073f43a..d0d451e110dfcc411187e6ccc15cc611629e7def 100644 (file)
@@ -2555,7 +2555,9 @@ testIframe(
        function( assert, framejQuery, frameWin, frameDoc ) {
                assert.expect( 1 );
 
-               var input = jQuery( frameDoc ).find( "#frame-input" );
+               var done = assert.async(),
+                       focus = false,
+                       input = jQuery( frameDoc ).find( "#frame-input" );
 
                // Create a focusin handler on the parent; shouldn't affect the iframe's fate
                jQuery( "body" ).on( "focusin.iframeTest", function() {
@@ -2563,23 +2565,56 @@ testIframe(
                } );
 
                input.on( "focusin", function() {
+                       focus = true;
                        assert.ok( true, "fired a focusin event in the iframe" );
                } );
 
                // Avoid a native event; Chrome can't force focus to another frame
-               input.trigger( "focusin" );
-
-               // Must manually remove handler to avoid leaks in our data store
-               input.remove();
-
-               // Be sure it was removed; nothing should happen
-               input.trigger( "focusin" );
+               input[ 0 ].focus();
 
                // Remove body handler manually since it's outside the fixture
                jQuery( "body" ).off( "focusin.iframeTest" );
+
+               setTimeout( function() {
+
+                       // DOM focus is unreliable in TestSwarm
+                       if ( QUnit.isSwarm && !focus ) {
+                               assert.ok( true, "GAP: Could not observe focus change" );
+                       }
+
+                       done();
+               }, 50 );
        }
 );
 
+QUnit.test( "focusin on document & window", function( assert ) {
+       assert.expect( 1 );
+
+       var counter = 0,
+               input = jQuery( "<input />" );
+
+       input.appendTo( "#qunit-fixture" );
+
+       input[ 0 ].focus();
+
+       jQuery( window ).on( "focusout", function() {
+               counter++;
+       } );
+       jQuery( document ).on( "focusout", function() {
+               counter++;
+       } );
+
+       input[ 0 ].blur();
+
+       // DOM focus is unreliable in TestSwarm
+       if ( QUnit.isSwarm && counter === 0 ) {
+               assert.ok( true, "GAP: Could not observe focus change" );
+       }
+
+       assert.strictEqual( counter, 2,
+               "focusout handlers on document/window fired once only" );
+} );
+
 testIframe(
        "jQuery.ready promise",
        "event/promiseReady.html",