You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React项目中react-dropdown-date组件焦点切换无法自动关闭的问题

解决React-dropdown-date点击外部输入框不收起下拉的问题

问题场景

在React项目中使用react-dropdown-date组件时,遇到以下问题:

  • 点击页面其他输入框时,日期选择的下拉框不会自动收起,仅能通过点击关闭按钮(X)或选择完整日期来关闭
  • 尝试在日期输入框的handelBlur方法中直接添加setIsOpen(false),会导致下拉框打开后立即关闭,无法进行日期选择

解决方案

核心思路是判断失焦/点击事件的目标是否属于日期选择容器内部,通过以下步骤实现:

1. 添加容器引用

在组件内部创建一个useRef,用来关联日期选择的外层容器:

import { useRef } from 'react'; // 确保导入useRef

const CustomField = ({...props}: ICustomField) => {
  // 新增容器ref
  const dateContainerRef = useRef<HTMLDivElement>(null);
  // 其他原有状态和逻辑不变
};

2. 修改失焦处理逻辑

更新handelBlur方法,通过延迟判断焦点位置,避免点击下拉内部元素时误关闭:

const handelBlur = () => {
  setTouch(true);
  // 延迟100ms执行,等待浏览器完成焦点切换
  setTimeout(() => {
    const activeElement = document.activeElement;
    // 如果当前焦点不在日期容器内,关闭下拉
    if (dateContainerRef.current && !dateContainerRef.current.contains(activeElement as Node)) {
      setIsOpen(false);
    }
  }, 100);
};

3. 给日期容器绑定ref

将创建的dateContainerRef绑定到包裹日期输入框和下拉组件的外层div上:

{type === "date" && (
  <div ref={dateContainerRef}> {/* 绑定ref */}
    <input
      name={name}
      ref={register}
      value={selectDate ? getCorrectDate(selectDate) : ''}
      onClick={() => setIsOpen(true)}
      onBlur={handelBlur}
      placeholder={placeholder}
      readOnly={true}
    />
    {isOpen &&
      <div className='dateDropDown-container-modal'>
        <div className='dateDropDown-close' onClick={() => setIsOpen(false)}>x</div>
        <YearPicker
          defaultValue={'Рік'}
          start={+formatDate(minDate).split('-')[0]}
          end={+formatDate(maxDate).split('-')[0]}
          reverse
          required={true}
          disabled={dateReadOnly}
          value={year}
          onChange={(year) => {
            setYear(year);
            setTouchYear(true);
          }}
          id={'year'}
          name={'year'}
          classes={touchYear ? 'dateDropDown-touch' : 'dateDropDown'}
          optionClasses={'dateDropDown-option'}
        />
        <MonthPicker
          defaultValue={'Місяць'}
          numeric
          short
          caps
          endYearGiven
          year={year}
          required={true}
          disabled={dateReadOnly}
          value={month}
          onChange={(month) => {
            setMonth(month);
            setTouchMonth(true);
          }}
          id={'month'}
          name={'month'}
          classes={touchMonth ? 'dateDropDown-touch' : 'dateDropDown'}
          optionClasses={'dateDropDown-option'}
        />
        <DayPicker
          defaultValue={'День'}
          year={year}
          month={month}
          endYearGiven
          required={true}
          disabled={dateReadOnly}
          value={day}
          onChange={(day) => {
            setDay(day);
            setTouchDay(true);
          }}
          id={'day'}
          name={'day'}
          classes={touchDay ? 'dateDropDown-touch' : 'dateDropDown'}
          optionClasses={'dateDropDown-option'}
        />
      </div>
    }
  </div>
)}

4. 补充全局点击监听(可选)

添加全局点击事件监听,确保点击页面任何非日期容器的区域都能关闭下拉:

useEffect(() => {
  const handleGlobalClick = (e: MouseEvent) => {
    if (dateContainerRef.current && !dateContainerRef.current.contains(e.target as Node)) {
      setIsOpen(false);
    }
  };

  document.addEventListener('click', handleGlobalClick);
  // 组件卸载时移除监听,避免内存泄漏
  return () => document.removeEventListener('click', handleGlobalClick);
}, []);

逻辑说明

  • setTimeout的作用是等待浏览器完成焦点切换,避免点击下拉选择器内部元素时,因输入框失焦直接触发关闭
  • contains方法用于判断当前点击/焦点元素是否属于日期容器内部,精准区分内部操作和外部操作
  • 全局点击监听作为补充,覆盖所有外部点击场景,确保下拉框能正确收起

内容的提问来源于stack exchange,提问作者Hunter91151

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:21:08