Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jQuery 3.4.0+ silences native focus handlers attached after jQuery ones #5015

Closed
IanSimpson opened this issue Feb 20, 2022 · 4 comments · Fixed by #5228
Closed

jQuery 3.4.0+ silences native focus handlers attached after jQuery ones #5015

IanSimpson opened this issue Feb 20, 2022 · 4 comments · Fixed by #5228

Comments

@IanSimpson
Copy link

IanSimpson commented Feb 20, 2022

Description

This may be related to the dicussion/resolution for #4856, but is a distinct issue.

If you have a Google Address autocomplete input, and trigger focus programatically (e.g. $('input').trigger('focus')) the autocomplete works fine. However, if you add any other focus event listener (event just an empty function, like $('input').on('focus', function() {})) then the autocomplete won't work until manually focused (e.g. through clicking or tabbing).

Link to test case

Test case is at https://jsfiddle.net/bgmrujdt/1/. First autocomplete has a focus event handler, focusing by clicking the button does not work. Second autocomplete doesn't have a focus event handler, focusing by clicking the button does work.

The same code with jQuery 3.3.1 works fine (example at https://jsfiddle.net/bgmrujdt/2/). It also works fine using native javascript .focus() instead of jQuery (https://jsfiddle.net/s2zmhb9t/)

@mgol
Copy link
Member

mgol commented Feb 21, 2022

Thanks for the report and the test case! Looks like a real issue that we should investigate along other focus-related issues.

I'm going to tentatively set the milestone to 3.6.1 mostly so that we look at it before the release but it's not guaranteed to be fixed there, especially as we still need to discover the cause.

@mgol mgol added this to the 3.6.1 milestone Feb 21, 2022
@mgol mgol added Bug Event Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. labels Feb 21, 2022
@timmywil timmywil removed the Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. label Feb 28, 2022
@timmywil timmywil modified the milestones: 3.6.1, 3.7.0 Aug 15, 2022
@mgol mgol added the Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. label Mar 17, 2023
@mgol
Copy link
Member

mgol commented Mar 17, 2023

The issue is reproducible without third party libraries. Attaching a jQuery focus handler on an element makes native handlers attached later not fire at all:

<input id="input">
<button id="button">Run the test</button>
var $button = $("#button");
var $input = $('#input');
var input = $input[0];

input.addEventListener('focus', function () {
	console.log('native focus handler 1');
});

$input.on("focus", function () {
	console.log("jQuery focus handler");
});

input.addEventListener('focus', function () {
	console.log('native focus handler 2');
});

$("#button").on("click", function () {
	$input.trigger("focus");
});

Only native focus handler 1 & jQuery focus handler will be logged.

Test case: https://jsbin.com/wupovat/1/edit?html,js,console,output

The reason is most likely the event.stopImmediatePropagation() call at the end of this block:

jquery/src/event.js

Lines 580 to 598 in cff2899

// 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.length ) {
// ...and capture the result
dataPriv.set( this, type, {
value: jQuery.event.trigger(
// 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();
}

This issue is not fixed by my PR #5223 which otherwise fixes a number of other focus issues.

@mgol mgol changed the title jQuery 3.4.0+ creates focus issues with Google autocompletes jQuery 3.4.0+ silences native focus handlers attached after jQuery ones Mar 20, 2023
@mgol
Copy link
Member

mgol commented Mar 20, 2023

BTW, since the click event uses leverageNative, it suffers from the same issues: https://jsbin.com/katowor/1/edit?html,js,console,output

mgol added a commit to mgol/jquery that referenced this issue Mar 25, 2023
In `leverageNative`, instead of calling `event.stopImmediatePropagation()`
which would abort both native & jQuery handlers, set the wrapper's
`isImmediatePropagationStopped` property to a function returning `true`.
Since for each element + type pair jQuery attaches only one native handler,
there is also only one wrapper jQuery event so this achieves the goal:
on the target element jQuery handlers don't fire but native ones do.

Unfortunately, this workaround doesn't work for handlers on ancestors
- since the native event is re-wrapped by a jQuery one on each level of
the propagation, the only way to stop it for jQuery was to stop it for
everyone via native `stopPropagation()`. This is not a problem for
`focus`/`blur` which don't bubble, but it does also stop `click` on
checkboxes and radios. We accept this limitation.

Fixes jquerygh-5015
@mgol
Copy link
Member

mgol commented Mar 26, 2023

PR: #5228

@timmywil timmywil removed the Discuss in Meeting Reserved for Issues and PRs that anyone would like to discuss in the weekly meeting. label Mar 27, 2023
mgol added a commit to mgol/jquery that referenced this issue Mar 27, 2023
In `leverageNative`, instead of calling `event.stopImmediatePropagation()`
which would abort both native & jQuery handlers, set the wrapper's
`isImmediatePropagationStopped` property to a function returning `true`.
Since for each element + type pair jQuery attaches only one native handler,
there is also only one wrapper jQuery event so this achieves the goal:
on the target element jQuery handlers don't fire but native ones do.

Unfortunately, this workaround doesn't work for handlers on ancestors
- since the native event is re-wrapped by a jQuery one on each level of
the propagation, the only way to stop it for jQuery was to stop it for
everyone via native `stopPropagation()`. This is not a problem for
`focus`/`blur` which don't bubble, but it does also stop `click` on
checkboxes and radios. We accept this limitation.

Fixes jquerygh-5015
mgol added a commit to mgol/jquery that referenced this issue Mar 27, 2023
In `leverageNative`, instead of calling `event.stopImmediatePropagation()`
which would abort both native & jQuery handlers, set the wrapper's
`isImmediatePropagationStopped` property to a function returning `true`.
Since for each element + type pair jQuery attaches only one native handler,
there is also only one wrapper jQuery event so this achieves the goal:
on the target element jQuery handlers don't fire but native ones do.

Unfortunately, this workaround doesn't work for handlers on ancestors
- since the native event is re-wrapped by a jQuery one on each level of
the propagation, the only way to stop it for jQuery was to stop it for
everyone via native `stopPropagation()`. This is not a problem for
`focus`/`blur` which don't bubble, but it does also stop `click` on
checkboxes and radios. We accept this limitation.

Fixes jquerygh-5015
mgol added a commit that referenced this issue Mar 27, 2023
In `leverageNative`, instead of calling `event.stopImmediatePropagation()`
which would abort both native & jQuery handlers, set the wrapper's
`isImmediatePropagationStopped` property to a function returning `true`.
Since for each element + type pair jQuery attaches only one native handler,
there is also only one wrapper jQuery event so this achieves the goal:
on the target element jQuery handlers don't fire but native ones do.

Unfortunately, this workaround doesn't work for handlers on ancestors
- since the native event is re-wrapped by a jQuery one on each level of
the propagation, the only way to stop it for jQuery was to stop it for
everyone via native `stopPropagation()`. This is not a problem for
`focus`/`blur` which don't bubble, but it does also stop `click` on
checkboxes and radios. We accept this limitation.

Fixes gh-5015
Closes gh-5228
mgol added a commit that referenced this issue Mar 27, 2023
In `leverageNative`, instead of calling `event.stopImmediatePropagation()`
which would abort both native & jQuery handlers, set the wrapper's
`isImmediatePropagationStopped` property to a function returning `true`.
Since for each element + type pair jQuery attaches only one native handler,
there is also only one wrapper jQuery event so this achieves the goal:
on the target element jQuery handlers don't fire but native ones do.

Unfortunately, this workaround doesn't work for handlers on ancestors
- since the native event is re-wrapped by a jQuery one on each level of
the propagation, the only way to stop it for jQuery was to stop it for
everyone via native `stopPropagation()`. This is not a problem for
`focus`/`blur` which don't bubble, but it does also stop `click` on
checkboxes and radios. We accept this limitation.

Fixes gh-5015
Closes gh-5228

(cherry picked from commit 6ad3651)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

3 participants