-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathonFocusGot.ts
39 lines (36 loc) · 1.44 KB
/
onFocusGot.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import onFocusLost from "./onFocusLost";
/** Fires when element or children gotFocus (happens once)
* Depsite on focusin onFocusGot event isn't called several times on children inside element
* @param {HTMLElement} element HTMLElement to apply `.addEventListener`
* @param {Function} listener Callback invoked on event
* @param options OnFocusLostOptions. onFocusGot depends on onFocusLost
* @return dispose() function. Call it to remove listener
* */
export default function onFocusGot(
element: HTMLElement,
listener: (this: HTMLElement, ev: HTMLElementEventMap["focusin"]) => any,
options?: Parameters<typeof onFocusLost>[2]
): () => void {
let isLost = true;
let removeLost: undefined | (() => void);
const remove = (): void => {
removeLost?.call(element);
element.removeEventListener("focusin", focusin);
};
const focusin = (e: FocusEvent): void => {
if (!isLost) {
return;
}
const isFocused = (a: Node): boolean => element === a || element.contains(a);
const isPrevFocused = e.relatedTarget instanceof Node && isFocused(e.relatedTarget);
// requires to detect focusLost properly (when console opens)
removeLost = removeLost || onFocusLost(element, () => (isLost = true), options);
if (!isPrevFocused) {
listener.call(element, e);
options?.once && remove();
isLost = false;
}
};
element.addEventListener("focusin", focusin, { passive: true, ...options });
return remove;
}