Dropdown下拉菜单点击后不消失怎么办?
我用原生JS写了个简单的下拉菜单,点击按钮显示菜单没问题,但点菜单项之后下拉框不自动收起,得再点别处才行。这体验太差了。
我试过在菜单项的点击事件里加 dropdown.classList.remove('show'),但有时候会报错说元素找不到,而且如果用户点了其他地方,又得另外处理关闭逻辑,感觉很乱。
document.querySelector('.dropdown-toggle').addEventListener('click', () => {
document.querySelector('.dropdown-menu').classList.toggle('show');
});
// 菜单项点击
document.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', () => {
// 这里想关掉菜单,但有时失效
document.querySelector('.dropdown-menu').classList.remove('show');
});
});
先说你代码里几个明显的问题。第一个大问题是每次点击事件里都重新用
document.querySelector去查DOM,这又慢又容易出问题,万一DOM结构变了或者还没加载完,直接报错。正确的做法是在代码开头就把元素缓存好。第二个问题是缺少点击外部关闭的逻辑,这个其实才是下拉菜单最核心的交互之一。
我给你写个完整方案,顺便把事件委托的优化也加上:
解释一下几个关键点。
e.stopPropagation()这个方法很关键,它会阻止事件向上冒泡。为什么要阻止?因为当你点击按钮或菜单项时,如果不阻止冒泡,事件会一路传到document,然后触发document上的点击事件,刚打开的菜单立马又关上了,就会出现"闪一下就没了"的问题。关于点击外部关闭的逻辑,用
contains()方法判断点击目标是否在菜单或按钮内部。如果点击的是菜单里的内容,那就不关闭;如果点的是外面,就关闭。这个判断比用closest()更直观一些。还有个优化点,如果你的菜单项是动态生成的,用事件委托会更合适,把事件绑在
dropdownMenu上,通过e.target判断点击的是哪个菜单项。对了,你之前报错"元素找不到"大概率是因为JS执行的时候DOM还没渲染完,要么把script标签放到body最后,要么用
DOMContentLoaded包一下:这样应该就能解决你的问题了,菜单项点击关闭、点击外部关闭都能正常工作。