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

ReactJS问题:输入框失焦导致下拉菜单按钮事件无法触发

问题:搜索框下拉菜单按钮点击失效

我需要在React中实现一个搜索框,输入内容时弹出下拉菜单,菜单内的按钮可触发事件。要求是当应用中其他输入框被使用时,该下拉菜单需消失。目前已实现该功能,但存在问题:点击下拉菜单按钮时,输入框先失焦导致下拉菜单消失,按钮事件无法触发。

Searchbox组件代码

import React, { useState } from 'react';
import Dropdown from './Dropdown';

function Search(props) {

    const [focused, setFocused] = useState(false);
    
    const inputHandler = (params) => {
        if (params.length > 0)
            props.apiCall(params);
    }
    
    const buttonHandler = (id) => {
        console.log(id);
    }
    
    return (
        <>
            <input
                type="text"
                placeholder="Suchen.."
                onChange={(event) => inputHandler(event.target.value)}
                onFocus={() => setFocused(true)}
                onBlur={() => setFocused(false)} // 问题所在
            />
            {
                focused === true && props.apiData.length > 0 ?
                    props.apiData.map((mappedData, key) => {
                        return (
                            <Dropdown
                                key={key}
                                id={mappedData.id}
                                name={mappedData.name}
                                /*
                                  even more Data
                                */
                                buttonHandler={buttonHandler}
                            />
                        )
                    })
                    : null
            }
        </>
    )

}

export default Search;
import React from 'react';

function Dropdown(props) {

    return (
        <ul key={props.id}>
            <li>{props.name}</li>
            <li>even more data</li>
            <li>
                <input
                    type="button"
                    value="Select"
                    onClick={() => {
                        props.buttonHandler(props.id)
                    }}
                />
            </li>
        </ul>
    )

}

export default Dropdown;

解决方案

这个问题的核心是输入框的onBlur事件会先于按钮的onClick事件触发,导致下拉菜单提前卸载,按钮点击事件无法执行。这里提供两种可行的解决思路:

方案1:给onBlur添加延迟,让按钮事件先执行

通过setTimeout延迟隐藏下拉菜单,同时在按钮点击时取消这个延迟,确保事件能正常触发。

修改Search组件的代码:

import React, { useState, useRef } from 'react';
import Dropdown from './Dropdown';

function Search(props) {

    const [focused, setFocused] = useState(false);
    const blurTimeoutRef = useRef(null); // 保存延迟ID
    
    const inputHandler = (params) => {
        if (params.length > 0)
            props.apiCall(params);
    }
    
    const buttonHandler = (id) => {
        // 取消延迟,防止下拉菜单消失
        clearTimeout(blurTimeoutRef.current);
        console.log(id);
        // 执行完逻辑后手动隐藏下拉菜单
        setFocused(false);
    }
    
    return (
        <>
            <input
                type="text"
                placeholder="Suchen.."
                onChange={(event) => inputHandler(event.target.value)}
                onFocus={() => {
                    // 聚焦时清除之前的延迟
                    clearTimeout(blurTimeoutRef.current);
                    setFocused(true);
                }}
                onBlur={() => {
                    // 延迟200毫秒隐藏下拉菜单,给按钮点击留执行时间
                    blurTimeoutRef.current = setTimeout(() => {
                        setFocused(false);
                    }, 200);
                }}
            />
            {
                focused === true && props.apiData.length > 0 ?
                    props.apiData.map((mappedData, key) => {
                        return (
                            <Dropdown
                                key={key}
                                id={mappedData.id}
                                name={mappedData.name}
                                buttonHandler={buttonHandler}
                            />
                        )
                    })
                    : null
            }
        </>
    )

}

export default Search;

方案2:监听全局点击,判断点击区域

通过监听document的点击事件,判断点击目标是否属于搜索框或下拉菜单,只有点击外部时才隐藏下拉菜单,避免内部点击触发隐藏。

修改Search组件的代码:

import React, { useState, useRef, useEffect } from 'react';
import Dropdown from './Dropdown';

function Search(props) {

    const [focused, setFocused] = useState(false);
    const searchContainerRef = useRef(null); // 包裹搜索框和下拉菜单的容器
    
    const inputHandler = (params) => {
        if (params.length > 0)
            props.apiCall(params);
    }
    
    const buttonHandler = (id) => {
        console.log(id);
        setFocused(false);
    }

    useEffect(() => {
        const handleClickOutside = (e) => {
            // 如果点击目标不在容器内,隐藏下拉菜单
            if (searchContainerRef.current && !searchContainerRef.current.contains(e.target)) {
                setFocused(false);
            }
        };
        // 添加全局点击监听
        document.addEventListener('mousedown', handleClickOutside);
        return () => {
            // 组件卸载时移除监听
            document.removeEventListener('mousedown', handleClickOutside);
        };
    }, []);
    
    return (
        <div ref={searchContainerRef}> {/* 添加容器ref */}
            <input
                type="text"
                placeholder="Suchen.."
                onChange={(event) => inputHandler(event.target.value)}
                onFocus={() => setFocused(true)}
                // 移除原来的onBlur
            />
            {
                focused === true && props.apiData.length > 0 ?
                    props.apiData.map((mappedData, key) => {
                        return (
                            <Dropdown
                                key={key}
                                id={mappedData.id}
                                name={mappedData.name}
                                buttonHandler={buttonHandler}
                            />
                        )
                    })
                    : null
            }
        </div>
    )

}

export default Search;

两种方案都能解决问题,方案2的体验更流畅,不会有延迟感,推荐优先使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 17:11:17