You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

关键逻辑说明

  1. DOM元素引用:通过ref获取触发按钮和下拉菜单的DOM节点,用于后续判断点击范围。
  2. 全局事件监听:在组件挂载时给document添加点击监听,组件销毁时移除,防止内存泄漏。
  3. 点击范围判断:在handleClickOutside中检查点击目标是否属于按钮或菜单区域,仅当点击外部时才关闭下拉。
  4. 兼容Modal组件:点击下拉内部的Modal(无论Modal是否挂载在菜单容器内)都不会触发下拉关闭,避免冲突。

内容的提问来源于stack exchange,提问作者ironman33

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 08:05:25