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

div绑定id/ref时document.activeElement判断失效 点击菜单外关闭功能异常

核心问题分析

  • 问题1:普通div元素默认无法获得焦点
    你用来包裹菜单的<div>默认不支持聚焦,哪怕手动调用focus()方法也无法让它成为document.activeElement,所以document.activeElement !== menuRef.current的判断永远为真。如果要让div可聚焦,需要给它添加tabIndex属性,比如tabIndex="-1"(表示只能通过js调用focus聚焦,不能通过tab键切换聚焦)。
  • 问题2:定时器id存储方式错误
    你在组件内部直接声明let closeTimeMenu存储定时器id,组件每次重新渲染时这个变量都会被重置,之前存的定时器id会丢失,导致clearTimeout根本无法正常清除定时器。需要用useRef来存储定时器id,useRef的引用值会跨渲染周期保持稳定。
  • 问题3:点击区域判断逻辑不合理
    用activeElement判断是否点击了菜单内部本身可靠性极低,更合理的方案是直接判断点击事件的触发节点是否被菜单容器包含,调用DOM元素的contains()方法即可实现。
  • 问题4:事件绑定与回调逻辑错误
    你代码中window.onclick = toggleMenu(false)的写法是错误的:toggleMenu(false)会被立即执行,而不是作为点击事件的回调触发,且不需要额外给window绑定点击事件,监听到外部点击后直接调用toggleMenu(false)即可。
  • 问题5:闭包与依赖遗漏问题
    你的useEffect依赖数组为空,但内部用到了toggleMenu方法,存在闭包陷阱,可能导致获取到的永远是初始渲染时的旧方法引用。

修正后的代码示例

import { useRef, useEffect } from 'react';
import { useMenuState, ControlledMenu, MenuItem } from '@szhsin/react-menu';

export default function Example() {
  const { toggleMenu } = useMenuState({ transition: true });
  // 用useRef存定时器id,跨渲染保持稳定
  const closeTimeMenu = useRef(null);
  const menuRef = useRef(null);

  useEffect(() => {
    function handleClickOutside(e) {
      // 直接判断点击目标是否在菜单容器内
      if (menuRef.current && !menuRef.current.contains(e.target)) {
        setTimeout(() => {
          // 清除定时器
          if (closeTimeMenu.current) {
            clearTimeout(closeTimeMenu.current);
          }
          toggleMenu(false);
        }, 500);
      }
    }

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [toggleMenu]); // 把toggleMenu加入依赖数组

  function timeout() {
    closeTimeMenu.current = setTimeout(function () {
      toggleMenu(false);
    }, 1500);
  }

  return (
    {/* 给div加tabIndex属性,支持聚焦(如果后续还有聚焦需求的话) */}
    <div ref={menuRef} id="myField" tabIndex="-1">
      <button
        onMouseEnter={() => {
          toggleMenu(true);
          if (closeTimeMenu.current) {
            clearTimeout(closeTimeMenu.current);
          }
        }}
        onMouseLeave={() => timeout()}
      >
        hover打开菜单
      </button>

      <ControlledMenu
        anchorRef={menuRef}
        onMouseEnter={() => {
          toggleMenu(true);
          if (closeTimeMenu.current) {
            clearTimeout(closeTimeMenu.current);
          }
        }}
        onItemClick={() => {
          toggleMenu(false);
        }}
        onMouseLeave={() => timeout()}
      >
        <MenuItem>保存</MenuItem>
        <MenuItem>关闭窗口</MenuItem>
      </ControlledMenu>
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 13:39:02