useFocusWithin
作用
监听当前焦点是否在某个区域之内,同 css 属性 :focus-within。
原理
监听
focusin
和focusout
方法。当元素即将失去焦点时,focusout
事件被触发。当元素即将聚焦时,focusin
事件被触发。它们和focus
和blur
方法的主要区别是后面两个不会进行冒泡。
源码
ts
import { useState } from 'react'
import useEventListener from '../useEventListener'
import type { BasicTarget } from '../utils/domTarget'
export interface Options {
onFocus?: (e: FocusEvent) => void
onBlur?: (e: FocusEvent) => void
onChange?: (isFocusWithin: boolean) => void
}
export default function useFocusWithin(target: BasicTarget, options?: Options) {
// 用于记录当前元素是否获取焦点
const [isFocusWithin, setIsFocusWithin] = useState(false)
const { onFocus, onBlur, onChange } = options || {}
// 监听 focusin
useEventListener(
'focusin',
(e: FocusEvent) => {
if (!isFocusWithin) {
// 当元素获取焦点时触发 onFocus
onFocus?.(e)
// 元素焦点变化时出发 onChange
onChange?.(true)
setIsFocusWithin(true)
}
},
{
target,
},
)
// 监听 focusout 事件
useEventListener(
'focusout',
(e: FocusEvent) => {
if (isFocusWithin && !(e.currentTarget as Element)?.contains?.(e.relatedTarget as Element)) {
// 当元素失去焦点时触发 onBlur
onBlur?.(e)
// 元素焦点变化时出发 onChange
onChange?.(false)
setIsFocusWithin(false)
}
},
{
target,
},
)
return isFocusWithin
}