React组件外部点击检测:实现导航栏下拉菜单外部点击关闭
实现点击外部区域关闭React响应式导航下拉菜单
问题说明
我在React应用中实现了响应式导航栏:屏幕尺寸缩小时,导航按钮隐藏,显示菜单图标;点击图标可展开下拉菜单。但目前仅能通过点击图标关闭菜单,需要实现点击屏幕任意区域或调整窗口大小时自动关闭下拉菜单。
解决方案
要实现需求,需要添加全局点击监听和窗口 resize 监听,同时处理事件冒泡避免冲突:
- 当菜单展开时,给
document绑定点击事件,判断点击目标是否在导航栏内部,若不在则关闭菜单 - 监听窗口尺寸变化,当宽度超过响应式断点(560px)时自动关闭菜单
- 点击菜单图标时阻止事件冒泡,避免触发全局点击事件直接关闭菜单
修改后的JSX代码
import React, { useEffect, useRef } from 'react'; import menu from '../images/menu.png'; export default function Nav() { const [isExpanded, setIsExpanded] = React.useState(false); const navRef = useRef(null); // 用于获取导航栏DOM元素 function toggleNav(e) { e.stopPropagation(); // 阻止事件冒泡,避免触发全局点击 setIsExpanded(!isExpanded); } // 全局点击监听:点击外部关闭菜单 useEffect(() => { function handleClickOutside(e) { if (navRef.current && !navRef.current.contains(e.target)) { setIsExpanded(false); } } // 窗口resize监听:超过断点关闭菜单 function handleResize() { if (window.innerWidth > 560) { setIsExpanded(false); } } if (isExpanded) { document.addEventListener('mousedown', handleClickOutside); window.addEventListener('resize', handleResize); } // 组件卸载或菜单关闭时移除监听 return () => { document.removeEventListener('mousedown', handleClickOutside); window.removeEventListener('resize', handleResize); }; }, [isExpanded]); return ( <nav className="navBar" ref={navRef}> <a href="#main" className="navMainLogo">Nick J's Code Blog!</a> <img src={menu} alt="menu icon" className="menuIcon" onClick={toggleNav}></img> <div className="navButtons" id={isExpanded ? "expanded" : ""}> <a className="firstButton" href="#feed">Blog Posts</a> <a href="google.com">About</a> <a href="google.com">My Portfolio</a> </div> </nav> ) }
CSS代码(保持不变)
/*nav Bar */ .navBar { height: 60px; width: 100%; position: fixed; display: flex; flex-direction: row; align-items: center; justify-content: space-between; font-family: "Montserrat", sans-serif; color: white; background-color: var(--color2); } .navMainLogo { margin-left: 30px; } .navButtons { width: 50%; } .navButtons { width: 50%; padding: 0; display: flex; flex-direction: row; justify-content: space-evenly; } .navButtons ul li { list-style: none; } .navBar a { color: white; text-decoration: none; } nav a:hover { color: var(--color4); } .menuIcon { margin-right: 30px; margin-left: auto; display: none; width: 20px; } .menuIcon:hover { cursor: pointer; } @media screen and (max-width: 560px) { .navBar .menuIcon { display: block; } .navButtons { display: none; } #expanded { margin-left: 50%; justify-content: center; align-items: center; background-color: var(--color2); position: absolute; display: flex; flex-direction: column; margin-top: 145px; } #expanded a { margin: 5px; } }
内容的提问来源于stack exchange,提问作者Nick Johnson
相关产品推荐
相关产品推荐

