Vue3中document.querySelector类切换后仍选中元素的问题求助
Vue3 实现侧边栏菜单开合(替代jQuery SlideUp/SlideDown)的问题
我想用Vue3实现类似jQuery的slideUp()/slideDown()的侧边栏菜单开合功能,但遇到了问题:多次点击后菜单刚打开就立即关闭,仿佛元素状态没有正确更新。
当前代码实现
Script 部分
export default { mounted() { let hasSubmenu = Array.from(document.querySelectorAll(".hasSubmenu.close")); let self = this; hasSubmenu.forEach(function (el) { el.addEventListener("click", self.menuOpen.bind(this, el)); }); }, methods:{ menuOpen(el){ let ul = el.querySelector("ul"); ul.style.height = `${ul.scrollHeight}px`; el.classList.remove("close"); el.classList.add("open"); this.menuClose(); }, menuClose(){ document.querySelector(".hasSubmenu.open").addEventListener("click", function (e) { e.currentTarget.querySelector("ul").style.height = "0px"; e.currentTarget.classList.remove("open"); e.currentTarget.classList.add("close"); }); } }, }
Template 部分
<ul id="main-nav"> <li class="hasSubmenu close"> <a href="#" class="menuItem"><div> Main Menu One</div></a> <ul style="height: 0px"> <li> <a href="#" class="py-3"> Sub Menu One</a> </li> <li> <a href="#" class="py-3"> Sub Menu Three</a> </li> </ul> </li> </ul>
问题原因
- 重复绑定事件:每次调用
menuOpen时,都会执行menuClose给当前元素绑定新的点击事件。多次点击后,元素上会积累多个点击回调,后续点击会同时触发展开和收起逻辑,导致菜单刚打开就关闭。 - 脱离Vue响应式:直接操作原生DOM和类名,没有利用Vue的数据驱动特性,状态管理混乱,容易出现DOM和数据不一致的问题。
解决方案
方案1:修复原生DOM逻辑(不推荐,不符合Vue思想)
避免重复绑定事件,改为单次绑定并判断元素状态:
export default { mounted() { document.querySelectorAll(".hasSubmenu").forEach(el => { el.addEventListener("click", this.toggleMenu); }); }, methods: { toggleMenu(e) { const el = e.currentTarget; const ul = el.querySelector("ul"); if (el.classList.contains("close")) { // 展开菜单 ul.style.height = `${ul.scrollHeight}px`; el.classList.replace("close", "open"); } else { // 收起菜单 ul.style.height = "0px"; el.classList.replace("open", "close"); } } } }
方案2:Vue响应式实现(推荐)
利用Vue的数据驱动和<Transition>组件实现平滑过渡,完全抛弃原生DOM操作:
完整单文件组件代码
<script setup> import { ref, onMounted } from 'vue'; // 管理菜单展开状态 const isMenuOpen = ref(false); // 引用子菜单DOM元素 const subMenu = ref(null); // 存储子菜单高度,用于过渡动画 const menuHeight = ref(0); onMounted(() => { if (subMenu.value) { menuHeight.value = subMenu.value.scrollHeight; // 设置CSS变量供过渡动画使用 document.documentElement.style.setProperty('--menu-height', `${menuHeight.value}px`); } }); // 切换菜单开合状态 const toggleMenu = () => { isMenuOpen.value = !isMenuOpen.value; }; </script> <template> <ul id="main-nav"> <li class="hasSubmenu" @click="toggleMenu"> <a href="#" class="menuItem"><div> Main Menu One</div></a> <Transition name="slide"> <ul ref="subMenu" v-show="isMenuOpen"> <li> <a href="#" class="py-3"> Sub Menu One</a> </li> <li> <a href="#" class="py-3"> Sub Menu Three</a> </li> </ul> </Transition> </li> </ul> </template> <style scoped> /* 实现slide过渡动画 */ .slide-enter-from, .slide-leave-to { height: 0; overflow: hidden; } .slide-enter-active, .slide-leave-active { transition: height 0.3s ease; } .slide-enter-to, .slide-leave-from { height: var(--menu-height); } </style>
这种方式完全遵循Vue的响应式思想,状态管理清晰,不会出现事件重复绑定的问题,同时通过<Transition>组件实现了平滑的滑动效果。
内容的提问来源于stack exchange,提问作者Rejown Ahmed
相关产品推荐
相关产品推荐

