Vue 3+Tailwind实现点击页面任意区域关闭下拉菜单
解决方案:点击页面任意区域关闭Vue3下拉菜单
要实现点击页面任意区域关闭下拉菜单,可通过监听全局点击事件,判断点击目标是否属于下拉触发按钮或菜单容器的范围,若不属于则关闭下拉。同时修复你代码里的一个小问题:模板中按钮class绑定的open变量未定义,需改为脚本里的listIsVisible。
修改后的完整代码
<template> <div class="relative bg-white"> <div class="mx-auto max-w-7xl px-4 sm:px-6"> <div class="flex items-center justify-between border-b-2 border-gray-100 py-6 md:justify-start md:space-x-10"> <div as="nav" class="hidden space-x-10 md:flex"> <div class="relative"> <!-- 给触发按钮添加ref,用于后续范围判断 --> <button @click="openList" ref="dropdownButton" :class="[listIsVisible ? 'text-gray-900' : 'text-gray-500', 'group inline-flex items-center rounded-md bg-white text-base font-medium hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2']" > <span>Analytics</span> <ChevronDownIcon :class="[listIsVisible ? 'text-gray-600' : 'text-gray-400', 'ml-2 h-5 w-5 group-hover:text-gray-500']" aria-hidden="true" /> </button> <transition v-if="listIsVisible" enter-active-class="transition ease-out duration-200" enter-from-class="opacity-0 translate-y-1" enter-to-class="opacity-100 translate-y-0" leave-active-class="transition ease-in duration-150" leave-from-class="opacity-100 translate-y-0" leave-to-class="opacity-0 translate-y-1"> <!-- 给下拉菜单容器添加ref --> <div ref="dropdownMenu" class="absolute z-10 -ml-4 mt-3 w-screen max-w-md transform px-2 sm:px-0 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2" > <div class="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5"> <div class="relative grid gap-6 bg-white px-5 py-6 sm:gap-8 sm:p-8"> <div class="ml-4"> <a href="/kgaps"> <p class="justify-end text-base font-medium text-gray-900 ml-2">Contract Gaps</p> <p class="text-sm text-gray-500 ml-2">Learn where there are gaps in contracts.</p> </a> <a href="/ftm"> <p class="justify-end text-base font-medium text-gray-900 ml-2 mt-6">FTM</p> <p class="text-sm text-gray-500 ml-2">Look at the massive amount of information this dashboard has.</p> </a> <subscriptions-modal></subscriptions-modal> </div> </div> </div> </div> </transition> </div> </div> </div> </div> </div> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue'; import { ChevronDownIcon } from '@heroicons/vue/20/solid' import SubscriptionsModal from './SubscriptionsModal' const listIsVisible = ref(false); // 定义ref用于获取按钮和菜单的DOM元素 const dropdownButton = ref(null); const dropdownMenu = ref(null); function openList() { listIsVisible.value = !listIsVisible.value } // 处理全局点击事件的函数 function handleClickOutside(event) { // 下拉未展开时直接返回 if (!listIsVisible.value) return; // 判断点击目标是否在按钮或菜单内部 const isInButton = dropdownButton.value?.contains(event.target); const isInMenu = dropdownMenu.value?.contains(event.target); // 点击外部则关闭下拉 if (!isInButton && !isInMenu) { listIsVisible.value = false; } } // 组件挂载时添加全局监听 onMounted(() => { document.addEventListener('click', handleClickOutside); }); // 组件销毁时移除监听,避免内存泄漏 onUnmounted(() => { document.removeEventListener('click', handleClickOutside); }); </script>
关键逻辑说明
- DOM元素引用:通过
ref获取触发按钮和下拉菜单的DOM节点,用于后续判断点击范围。 - 全局事件监听:在组件挂载时给
document添加点击监听,组件销毁时移除,防止内存泄漏。 - 点击范围判断:在
handleClickOutside中检查点击目标是否属于按钮或菜单区域,仅当点击外部时才关闭下拉。 - 兼容Modal组件:点击下拉内部的Modal(无论Modal是否挂载在菜单容器内)都不会触发下拉关闭,避免冲突。
内容的提问来源于stack exchange,提问作者ironman33
相关产品推荐
相关产品推荐

