Skip to content

Commit f1dc0b2

Browse files
committed
fix(utils): update to track pointer instead of keyboard
1 parent 0ded8ba commit f1dc0b2

File tree

1 file changed

+21
-27
lines changed

1 file changed

+21
-27
lines changed

core/src/utils/focus-visible.ts

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,51 +22,49 @@ export interface FocusVisibleUtility {
2222

2323
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
2424
let currentFocus: Element[] = [];
25-
let keyboardMode = false;
26-
let lastPointerType: string | null = null;
25+
// Tracks if the last interaction was a pointer event (mouse, touch, pen)
26+
// Used to distinguish between pointer and keyboard navigation for focus styling
27+
let hadPointerEvent = false;
2728

2829
const ref = rootEl ? rootEl.shadowRoot! : document;
2930
const root = rootEl ? rootEl : document.body;
3031

32+
// Adds or removes the focused class for styling
3133
const setFocus = (elements: Element[]) => {
3234
currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
3335
elements.forEach((el) => el.classList.add(ION_FOCUSED));
3436
currentFocus = elements;
3537
};
3638

39+
// Do not set focus on pointer interactions
3740
const pointerDown = (ev: Event) => {
38-
const pointerEvent = ev as PointerEvent;
39-
lastPointerType = pointerEvent.pointerType;
40-
keyboardMode = false;
41-
setFocus([]);
41+
if (ev instanceof PointerEvent && ev.pointerType !== '') {
42+
hadPointerEvent = true;
43+
// Reset after the event loop so only the immediate focusin is suppressed
44+
setTimeout(() => { hadPointerEvent = false; }, 0);
45+
}
4246
};
4347

48+
// Clear hadPointerEvent so keyboard navigation shows focus
49+
// Also, clear focus if the key is not a navigation key
4450
const onKeydown = (ev: Event) => {
45-
const keyboardEvent = ev as KeyboardEvent;
46-
// Always set keyboard mode to true when any key is pressed
47-
// This handles the WebKit Tab key bug where keydown might not fire
48-
keyboardMode = true;
51+
hadPointerEvent = false;
4952

50-
// If it's not a focus key, clear focus immediately
53+
const keyboardEvent = ev as KeyboardEvent;
5154
if (!FOCUS_KEYS.includes(keyboardEvent.key)) {
5255
setFocus([]);
5356
}
5457
};
5558

59+
// Set focus if the last interaction was NOT a pointer event
60+
// This works around iOS/Safari bugs where keydown is not fired for Tab
5661
const onFocusin = (ev: Event) => {
57-
// Check if this focus event is likely from keyboard navigation
58-
// We can detect this by checking if there was a recent keydown event
59-
// or if the focus target is a focusable element that typically gets focus via keyboard
6062
const target = ev.target as HTMLElement;
61-
62-
if (target.classList.contains(ION_FOCUSABLE)) {
63-
// If we're in keyboard mode or this looks like keyboard navigation
64-
if (keyboardMode || !lastPointerType) {
65-
const toFocus = ev.composedPath().filter((el): el is HTMLElement => {
66-
return el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE);
67-
});
68-
setFocus(toFocus);
69-
}
63+
if (target.classList.contains(ION_FOCUSABLE) && !hadPointerEvent) {
64+
const toFocus = ev.composedPath().filter((el): el is HTMLElement =>
65+
el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE)
66+
);
67+
setFocus(toFocus);
7068
}
7169
};
7270

@@ -80,16 +78,12 @@ export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility =>
8078
ref.addEventListener('focusin', onFocusin);
8179
ref.addEventListener('focusout', onFocusout);
8280
ref.addEventListener('pointerdown', pointerDown, { passive: true });
83-
ref.addEventListener('touchstart', pointerDown, { passive: true });
84-
ref.addEventListener('mousedown', pointerDown);
8581

8682
const destroy = () => {
8783
ref.removeEventListener('keydown', onKeydown);
8884
ref.removeEventListener('focusin', onFocusin);
8985
ref.removeEventListener('focusout', onFocusout);
9086
ref.removeEventListener('pointerdown', pointerDown);
91-
ref.removeEventListener('touchstart', pointerDown);
92-
ref.removeEventListener('mousedown', pointerDown);
9387
};
9488

9589
return {

0 commit comments

Comments
 (0)