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
相关产品推荐
相关产品推荐

