Skip to content

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
  )
}

如有转载或 CV 的请标注本站原文地址