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

React中利用onFocus和onBlur实现表单字段显隐的问题

解决折叠表单切换内部字段时意外收起的问题

你的核心问题是表单内元素切换焦点时,blur事件先于新元素的focus事件触发,导致误判为表单失焦并收起额外字段。原方案里的setTimeout失效是因为闭包捕获了旧的状态值,无法获取最新焦点状态。

先修正原代码里的两个明显错误:

  • other_input的onChange逻辑错误,误将值更新到message字段,应该根据name动态更新对应状态
  • 受控组件未绑定value属性,导致视图和状态不同步

下面是可行的解决方案,通过ref结合document.activeElement来准确判断焦点是否仍在表单内部:

import { useState, useRef } from 'react';

const defaultFormState = {
  message: '',
  other_input: ''
};

const FoldableForm = () => {
  const [formState, setFormState] = useState(defaultFormState);
  const [isExpanded, setIsExpanded] = useState(false);
  const formRef = useRef(null);

  const handleFocus = () => {
    setIsExpanded(true);
  };

  const handleBlur = () => {
    // 延迟100ms检查,确保内部元素的focus事件已触发
    setTimeout(() => {
      // 判断当前焦点是否仍在表单范围内
      const isFocusInside = formRef.current?.contains(document.activeElement);
      // 只有当焦点不在表单内,且两个字段都为空时才收起
      if (!isFocusInside && !formState.message.trim() && !formState.other_input.trim()) {
        setIsExpanded(false);
      }
    }, 100);
  };

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    // 使用函数式更新确保获取最新状态
    setFormState(prev => ({ ...prev, [name]: value }));
  };

  return (
    <form
      ref={formRef}
      onFocus={handleFocus}
      onBlur={handleBlur}
    >
      <textarea 
        name="message" 
        value={formState.message}
        onChange={handleInputChange}
      />
      {isExpanded && (
        <>
          <input 
            type="text" 
            name="other_input" 
            value={formState.other_input}
            onChange={handleInputChange}
          />
          <button type="button">Post comment</button>
        </>
      )}
    </form>
  );
};

方案说明:

  1. 表单Ref:通过formRef获取表单DOM元素,用于后续判断焦点是否在表单内部
  2. 延迟检查焦点:用setTimeout延迟100ms执行判断逻辑,确保切换焦点时新元素的focus事件已经触发,此时document.activeElement会指向新的输入框
  3. 焦点范围判断:通过formRef.current.contains(document.activeElement)确认焦点是否仍在表单内,避免误判
  4. 状态更新优化:使用函数式更新setFormState(prev => ...)确保每次更新都基于最新的状态值

这样处理后,在表单内切换输入框时,延迟检查会发现焦点仍在表单内,不会触发收起逻辑;只有当点击表单外部且字段为空时,才会收起额外字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 07:45:29