Dropdown下拉菜单点击外部区域无法收起怎么办?
我用React写了一个带下拉菜单的组件,给document绑定了click事件来收起菜单,但有时候点击菜单内部链接也会触发收起,导致链接点不动。试过加stopPropagation也不行,代码大概是这样:
function Dropdown() {
const [open, setOpen] = useState(false);
const ref = useRef();
useEffect(() => {
const handleClick = (e) => {
if (!ref.current.contains(e.target)) setOpen(false);
};
document.body.addEventListener('click', handleClick);
return () => document.body.removeEventListener('click', handleClick);
// 这里好像有问题?
}, []);
return (
<div ref={ref}>
<button onClick={() => setOpen(!open)}>Toggle</button>
{open && <ul>
<li><a href="#!" onClick={(e)=>e.stopPropagation()}>选项1</a></li>
</ul>}
</div>
);
}
现在点击选项1链接时,菜单会同时关闭,导致链接无法触发。明明给a标签加了stopPropagation啊,到底是哪里漏了?
这里有两个解决方案:
1. 简单粗暴的,改用mousedown事件而不是click:
因为React的click事件是在mousedown之后触发的,这样能确保在React事件处理之前就判断是否要关闭菜单。
2. 更标准的做法是用setTimeout延迟执行关闭逻辑:
这样会让React的合成事件先处理完再执行我们的逻辑。
我个人推荐第一种方案,改个事件类型就行,不需要额外处理时间差。遇到过好几次这种鬼打墙的问题,都是事件触发时机搞的鬼...
stopPropagation只阻止了子元素的冒泡,但你绑的是document的click事件,它还是会触发。改成这样:关键点是在
button和a标签上都加上stopPropagation,并且确保ref.current存在再调用contains,防止内存泄漏。别忘了给a标签加上具体的点击处理逻辑。