From: Michał Gołębiowski-Owczarek Date: Mon, 6 Apr 2020 18:34:40 +0000 (+0200) Subject: Event: Use only one focusin/out handler per matching window & document X-Git-Tag: 3.5.0~5 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9e15d6b469556eccfa607c5ecf53b20c84529125;p=jquery.git Event: Use only one focusin/out handler per matching window & document 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 --- diff --git a/src/event/focusin.js b/src/event/focusin.js index 7faef2981..3da86c787 100644 --- a/src/event/focusin.js +++ b/src/event/focusin.js @@ -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 ) { diff --git a/test/unit/event.js b/test/unit/event.js index ea9b67f16..d0d451e11 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -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.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",