useClickAway
作用
监听目标元素外的点击事件。
原理
通过
useRef
获取目标元素的ref
,然后通过useEffect
监听document
或者shadowDom
的事件默认为点击事件,当触发事件不是目标元素的子元素时,执行回调函数。
源码
typescript
import useLatest from '../useLatest'
import type { BasicTarget } from '../utils/domTarget'
import { getTargetElement } from '../utils/domTarget'
import getDocumentOrShadow from '../utils/getDocumentOrShadow'
import useEffectWithTarget from '../utils/useEffectWithTarget'
type DocumentEventKey = keyof DocumentEventMap
export default function useClickAway<T extends Event = Event>(
onClickAway: (event: T) => void,
target: BasicTarget | BasicTarget[],
eventName: DocumentEventKey | DocumentEventKey[] = 'click'
) {
// 保存回调函数
const onClickAwayRef = useLatest(onClickAway)
useEffectWithTarget(
() => {
const handler = (event: any) => {
const targets = Array.isArray(target) ? target : [target]
// 如果目标元素包含点击的元素,则不执行回调函数
if (
targets.some((item) => {
const targetElement = getTargetElement(item)
return !targetElement || targetElement.contains(event.target)
})
) {
return
}
// 执行回调函数
onClickAwayRef.current(event)
}
// 获取 document 或者 shadowDom
const documentOrShadow = getDocumentOrShadow(target)
// 获取事件类型
const eventNames = Array.isArray(eventName) ? eventName : [eventName]
// 添加事件监听
eventNames.forEach((event) => documentOrShadow.addEventListener(event, handler))
// 组件卸载时移除事件监听
return () => {
eventNames.forEach((event) => documentOrShadow.removeEventListener(event, handler))
}
},
Array.isArray(eventName) ? eventName : [eventName],
target
)
}