Summary
When focus is programmatically moved outside an active modalizer (e.g. via
element.focus() from application code), and the user then clicks on an
element inside that same modalizer, the element briefly receives focus and then
immediately loses it.
Tabster version
8.7.0
Steps to reproduce
- Open a popover with
trapFocus enabled (e.g. Fluent UI <Popover trapFocus>),
focus is inside it.
- Call
element.focus() on an element outside the popover from application
code (e.g. a keyboard shortcut handler moves focus to a sidebar element).
- The popover remains visible. Tabster calls
setActive(undefined) because the
target is not inside any modalizer — the popup's modalizer is now deactivated.
- Click on an input inside the still-visible popover.
- Expected: input receives and retains focus.
- Actual: input gets focus for ~100ms then immediately loses it.
Root cause
Step A — programmatic focus deactivates the modalizer.
isFocusedProgrammatically === true + target outside any modalizer → setActive(undefined)
→ this.activeId = undefined.
Step B — the user click is not recognized as targeting the active modal.
isFocusedProgrammatically === false, modalizer.userId !== activeId (undefined),
none of the allow-conditions pass → _restoreModalizerFocus is scheduled in 100ms.
Step C — _restoreModalizerFocus blurs the input.
The early-exit guard:
if (!modalizer && !activeId || modalizer && activeId === modalizer.userId) {
return;
}
With modalizer truthy (input is inside the popup) and activeId === undefined:
!modalizer && !activeId → false
modalizer && activeId === modalizer.userId → false
Neither exits. findFirst({ useActiveModalizer: true }) returns undefined
(no active modalizer to search within) → falls through to:
outsideElement.blur(); // blurs the input the user just clicked
Workaround
Block the programmatic focus movement before it happens (e.g. prevent keyboard
shortcuts from firing while a trap-focus popup is open). Incomplete as a general
solution since any element.focus() call can trigger the same deactivation.
Summary
When focus is programmatically moved outside an active modalizer (e.g. via
element.focus()from application code), and the user then clicks on anelement inside that same modalizer, the element briefly receives focus and then
immediately loses it.
Tabster version
8.7.0
Steps to reproduce
trapFocusenabled (e.g. Fluent UI<Popover trapFocus>),focus is inside it.
element.focus()on an element outside the popover from applicationcode (e.g. a keyboard shortcut handler moves focus to a sidebar element).
setActive(undefined)because thetarget is not inside any modalizer — the popup's modalizer is now deactivated.
Root cause
Step A — programmatic focus deactivates the modalizer.
isFocusedProgrammatically === true+ target outside any modalizer →setActive(undefined)→
this.activeId = undefined.Step B — the user click is not recognized as targeting the active modal.
isFocusedProgrammatically === false,modalizer.userId !== activeId(undefined),none of the allow-conditions pass →
_restoreModalizerFocusis scheduled in 100ms.Step C —
_restoreModalizerFocusblurs the input.The early-exit guard:
With
modalizertruthy (input is inside the popup) andactiveId === undefined:!modalizer && !activeId→ falsemodalizer && activeId === modalizer.userId→ falseNeither exits.
findFirst({ useActiveModalizer: true })returnsundefined(no active modalizer to search within) → falls through to:
Workaround
Block the programmatic focus movement before it happens (e.g. prevent keyboard
shortcuts from firing while a trap-focus popup is open). Incomplete as a general
solution since any
element.focus()call can trigger the same deactivation.