React中onBlur事件导致下拉框误隐藏,如何实现点击容器外才隐藏子元素
自定义输入组件点击下拉项误触发关闭的解决方案
问题根因
你当前绑定在外层容器的onBlur事件会在任意内部元素失去焦点时触发:点击下拉框内的选项按钮时,输入框会先触发失焦事件,外层的onBlur回调直接把下拉框隐藏,导致按钮的点击逻辑还没执行就被提前隐藏,不符合预期。
修改方案
通过判断失焦后的目标元素是否在组件容器内部,来决定是否隐藏下拉框,具体修改如下:
- 引入
useRef给外层容器绑定DOM引用,用于做元素包含判断 - 重写
onBlur回调逻辑,仅当失焦后的新焦点元素不在组件容器内时,才隐藏下拉框
修改后的完整代码:
import { useState, useRef } from 'react'; export const ListBoxInput = ({ label, data, keyName }) => { const [selected, setSelected] = useState({ key: null, value: null }); const [showDropdown, setShowDropdown] = useState(false); // 新增容器引用 const containerRef = useRef(null); return ( <div ref={containerRef} className='flex items-center space-x-3' // 重写onBlur逻辑 onBlur={(e) => { const nextFocusTarget = e.relatedTarget; // 新焦点在当前容器内部时,不关闭下拉框 if (nextFocusTarget && containerRef.current.contains(nextFocusTarget)) { return; } setShowDropdown(false); }} > <label className='block text-sm font-medium text-gray-700'>{label}</label> <div className='relative group'> <input type='text' value={selected.key} onFocus={() => setShowDropdown(true)} onChange={(e) => setSelected({ key: e.target.value, value: null })} disabled={!data.length} placeholder={!data.length ? 'No data...' : ''} className='w-full py-2 pl-3 pr-10 mt-1 text-left bg-white border border-gray-300 rounded-md shadow-sm disabled:opacity-50 focus:outline-none focus:ring-1 focus:ring-brand focus:border-brand sm:text-sm' /> {showDropdown && ( <div className='absolute z-10 w-full py-1 mt-3 overflow-auto text-base bg-white rounded-md shadow-lg max-h-56 ring-1 ring-black ring-opacity-5 focus:outline-none '> {data.map((r) => ( <button key={r.id} type='button' onClick={() => { setSelected({ key: r[keyName], value: r.id }); setShowDropdown(false); }} className='block w-full py-2 pl-3 text-left hover:bg-gray-100 pr-9' > {r[keyName]} </button> ))} </div> )} </div> </div> ); };
说明
- 点击下拉选项时,按钮属于容器内部元素,
onBlur判断后不会执行关闭逻辑,按钮的点击事件会正常执行,选中后主动关闭下拉框,符合预期 - 点击容器外部空白区域时,失焦后的
relatedTarget为null,会正常触发关闭逻辑,符合需求 - 该方案仅适用于下拉框DOM直接挂载在当前组件容器内部的场景,如果你后续改用Portal把下拉框挂载到body下,需要调整判断逻辑适配。
内容的提问来源于stack exchange,提问作者Xaarth
相关产品推荐
相关产品推荐

