Skip to content

useFocusWithin

作用

监听当前焦点是否在某个区域之内,同 css 属性 :focus-within。

原理

监听 focusinfocusout 方法。当元素即将失去焦点时,focusout 事件被触发。当元素即将聚焦时,focusin 事件被触发。它们和 focusblur 方法的主要区别是后面两个不会进行冒泡。

源码

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
}

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