问题描述
假设我有下面的代码结构。如果我将一个元素放在我的红色容器(输入之一)中,则会触发 focusin
事件。同样,如果我在红色容器外点击,focusout
事件会触发。
但是,如果我点击其中一个输入元素,然后直接点击另一个,则会快速连续触发 focusout
和 focusin
事件。
是否有一种简单的方法可以避免这种情况,或者找出是否可以忽略第二个 focusout
事件,因为焦点实际上停留在相关元素内,除了丑陋的解决方案(例如在第一个 {{1} 上设置标志) }} 事件并等待渲染刻度以查看是否发生另一个 focusout
事件?
focusin
document.getElementById("el").addEventListener("focusin",() => document.getElementById("out").innerHTML += "focusin<br/>");
document.getElementById("el").addEventListener("focusout",() => document.getElementById("out").innerHTML += "focusout<br/>");
解决方法
由于似乎没有一个简单的解决方案,我编写了等待下一个渲染滴答的丑陋解决方案。我把它写成可重用的 React 钩子,所以如果它对某人有帮助,那就在这里。
export const useFocusWithin = (
element: HTMLElement | undefined,onFocusIn?: () => void,onFocusOut?: () => void,) => {
const [focusWithin,setFocusWithin] = useState(false);
const isLoosingFocusFlag = useRef(false);
useHtmlElementEventListener(element,'focusin',() => {
setFocusWithin(true);
onFocusIn?.();
if (isLoosingFocusFlag.current) {
isLoosingFocusFlag.current = false;
}
});
useHtmlElementEventListener(element,'focusout',(e) => {
isLoosingFocusFlag.current = true;
setTimeout(() => {
if (isLoosingFocusFlag.current) {
onFocusOut?.();
isLoosingFocusFlag.current = false;
setFocusWithin(false);
}
});
});
return focusWithin;
};
export const useHtmlElementEventListener = <K extends keyof HTMLElementEventMap>(
element: HTMLElement | undefined,type: K,listener: (this: HTMLElement,ev: HTMLElementEventMap[K]) => any) => {
useEffect(() => {
if (element) {
element.addEventListener(type,listener as any);
return () => element.removeEventListener(type,listener as any);
}
},[element,listener,type]);
};