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

Next.js/React/Tailwind响应式菜单点击图标无法关闭问题求助

Next.js 响应式导航栏汉堡图标无法关闭的问题解决

我是Next.js和React新手,正在开发一款点击汉堡图标可展开/收起、点击外部可收起的响应式导航栏。目前遇到的问题是,汉堡图标点击打开菜单后,再次点击无法关闭,尽管我认为逻辑是正确的。

相关代码

MobileMenu组件

// MobileMenu组件代码
import { NAV_LINKS } from "@/constants";
import Image from "next/image";
import Link from "next/link";
import { useState, useEffect, useRef } from "react";

const MobileMenu = () => {
    const [active, setActive] = useState(false);
    const menuRef = useRef<HTMLUListElement>(null);

    useEffect(() => {
      const handleClickOutside = (event: MouseEvent) => {
        if (menuRef.current && !(menuRef.current as Node).contains(event.target as Node)) {
            setActive(false);
        }
      };

      document.body.addEventListener('click', handleClickOutside);

      return () => {
          document.body.removeEventListener('click', handleClickOutside);
      };
    }, []);

    const handleToggle = () => {
      setActive(prevActive => !prevActive);
    };
  
  return (
    <div className='lg:hidden'>
        {/* Hamburger Icon */}
        <div className="cursor-pointer">
            <Image
              src='/burger-menu.svg'
              alt='hamburger menu icon'
              width={34}
              height={34}
              onClick={handleToggle}
              className={active ? 'animateBurger' : ''}
            />
        </div>
        <div className="flex flex-col">
          <ul ref={menuRef} className={active ? 'animateMobileMenu' : 'lg:hidden hidden'}>
            {NAV_LINKS.map((link) => (
              <Link href={link.href} key={link.key}>
                {link.label}
              </Link>
            ))}
          </ul>
        </div>
    </div>
  )
}

export default MobileMenu

globals.css 样式

.animateBurger{
  @apply transition rotate-90 opacity-25
}

.animateMobileMenu{
  @apply transition-all p-24 flex justify-center items-center flex-col z-50 fixed top-20 left-0 w-full gap-12 text-white-10 regular-16 bg-black-100/75 backdrop-blur-sm
}

问题原因

问题出在事件冒泡和点击外部的监听逻辑冲突:

  1. 点击汉堡图标时,handleToggle执行,将active切换为true(打开菜单)。
  2. 但点击事件会向上冒泡到document.body,触发handleClickOutside函数。
  3. 此时汉堡图标并不在menuRef指向的<ul>元素内,所以handleClickOutside的条件成立,立即将active设回false,抵消了之前的切换操作,导致菜单看起来“无法关闭”。

修复方案

方案一:阻止汉堡图标点击事件的冒泡

修改handleToggle函数,阻止事件向上冒泡到body,这样点击汉堡图标时就不会触发handleClickOutside:

const handleToggle = (event: React.MouseEvent) => {
  event.stopPropagation(); // 阻止事件冒泡
  setActive(prevActive => !prevActive);
};

React会自动给onClick回调传递事件对象,无需额外修改Image组件的调用。

方案二:扩展menuRef的范围到汉堡图标容器

把menuRef绑定到包含汉堡图标和菜单的外层div,这样点击汉堡图标时,事件目标属于menuRef内部,handleClickOutside不会触发关闭逻辑:

const MobileMenu = () => {
    const [active, setActive] = useState(false);
    // 修改ref类型为HTMLDivElement
    const menuRef = useRef<HTMLDivElement>(null);

    // 其余逻辑保持不变

    return (
      {/* 将ref绑定到外层div */}
      <div className='lg:hidden' ref={menuRef}>
          {/* Hamburger Icon */}
          <div className="cursor-pointer">
              <Image
                src='/burger-menu.svg'
                alt='hamburger menu icon'
                width={34}
                height={34}
                onClick={handleToggle}
                className={active ? 'animateBurger' : ''}
              />
          </div>
          <div className="flex flex-col">
            <ul className={active ? 'animateMobileMenu' : 'lg:hidden hidden'}>
              {NAV_LINKS.map((link) => (
                <Link href={link.href} key={link.key}>
                  {link.label}
                </Link>
              ))}
            </ul>
          </div>
      </div>
    )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 07:03:30