在容器内更改焦点时,聚焦触发器不正确



假设我有下面的代码结构。如果我聚焦红色容器(其中一个输入(中的一个元素,就会触发focusin事件。同样,如果我在红色容器外单击,就会触发focusout事件。

但是,如果我单击其中一个输入元素,然后直接单击另一个,则focusoutfocusin事件都会快速连续触发。

有没有一种简单的方法可以避免这种情况,或者找出第二个focusout事件是否可以被忽略,因为焦点实际上停留在相关元素内,除了在第一个focusout事件上设置标志并等待渲染记号以查看是否发生另一个focusin事件等丑陋的解决方案之外?

document.getElementById("el").addEventListener("focusin", 
() => document.getElementById("out").innerHTML += "focusin<br/>");
document.getElementById("el").addEventListener("focusout", 
() => document.getElementById("out").innerHTML += "focusout<br/>");
<div id="el" style="background-color: red; padding: 4px">
<input />
<input />
</div>
<div id="out">
</div>

由于似乎没有一个简单的解决方案,我已经编写了等待下一次渲染的丑陋解决方案。我把它写成了可重复使用的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]);
};

最新更新