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 }
问题原因
问题出在事件冒泡和点击外部的监听逻辑冲突:
- 点击汉堡图标时,
handleToggle执行,将active切换为true(打开菜单)。 - 但点击事件会向上冒泡到
document.body,触发handleClickOutside函数。 - 此时汉堡图标并不在
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
相关产品推荐
相关产品推荐

