Vue下拉菜单点击收起失效:从blur到mousedown的事件顺序排查
本文记录在Vue开发中实现搜索联想下拉菜单时遇到的交互问题:点击列表项时onSearch未触发,仅执行了input的blur导致列表收起。通过验证事件触发顺序,发现blur优先级高于click。最终通过将列表项的click事件替换为mousedown事件,成功解决了点击失效问题,并提供了相关代码示例。
背景:
问题背景与初步尝试
近期开发一项功能:在搜索框输入时展示联想词下拉列表,点击联想词跳转至搜索结果页,点击其他区域则收起下拉列表。最初尝试利用input的失焦(blur)事件来触发收起操作,以避免监听整个body,但随即遇到了意料之外的Bug。下文将还原问题场景,并逐步展示解决方案(示例代码基于Vue框架实现)。
带有bug的版本演示图

注:此处搜索跳转逻辑已简化为console输出,实际并未执行跳转操作。
问题抛出
当点击上方的联想词时,预期的onSearch方法并未执行。
demo代码展示
<template lang="html">
<div :class="[baseClass + '-wrap']">
<input
type="text"
v-model="searchVal"
:class="[baseClass + '-input']"
ref="demo-search-input"
placeholder="搜索"
@focus="onFoucs"
@blur="onBlur"
@keyup.enter="onSearch"
@input="getRecommendedList" />
<span :class="[baseClass + '-btn']" @click="onSearch"></span>
<div :class="[baseClass + '-recommended']" v-show="isShowRecommend && recommendList.length > 0">
<div :class="[baseClass + '-triangle-border', baseClass + '-tb-border']"></div>
<div :class="[baseClass + '-triangle-border', baseClass + '-tb-bg']"></div>
<ul :class="[baseClass + '-list-wrap']">
<li
:class="[baseClass + '-list']"
v-for="(item, index) in recommendList"
:key="index"
@click="onSearch(item)"
>{{item}}</li>
</ul>
</div>
</div>
</template>
<script>
const mockData = ['123456', '12', '56873', '092341', '454666677']
export default {
data () {
return {
baseClass: 'demo-search',
searchVal: '',
isShowRecommend: false,
recommendList: []
}
},
methods: {
onFoucs () {
this.isShowRecommend = true
},
onBlur () {
this.isShowRecommend = false
this.searchVal = ''
this.recommendList = []
},
onSearch (val) {
val = typeof val === 'string' ? val : this.searchVal
if (val) {
// 这里需要跳转搜索,我们用console来代替
console.log(val)
this.searchVal = ''
} else {
this.$refs['demo-search-input'].focus()
}
},
getRecommendedList () {
if (this.searchVal) {
// 这里需要给后台发送请求来获取联想词,这里我们用mock数据匹配来展示
setTimeout(() => {
const reg = new RegExp(this.searchVal)
const arr = []
for (let i = 0; i < mockData.length; i++) {
if (reg.test(mockData[i])) {
arr.push(mockData[i])
}
}
this.recommendList = arr
}, 10)
}
}
}
}
</script>
Bug复现与代码逻辑分析
上述代码实现了该效果的基本逻辑:在input元素上绑定blur事件以清空搜索并收起列表,同时在列表(list)上绑定click事件。预期逻辑是:点击列表项时执行console输出并触发input失去焦点从而收起列表。然而实际运行结果仅为触发了blur事件,onSearch中的console并未执行。
猜测原因做尝试
事件顺序验证与原因分析
首先想到的原因是事件触发顺序问题,因此使用setTimeout工具进行验证。
onBlur () {
setTimeout(() => {
this.isShowRecommend = false
this.searchVal = ''
this.recommendList = []
}, 500)
}
验证结果证实了触发顺序的存在:由于设置了延迟,blur先于click执行。但当点击其他区域时,下拉窗口需要短暂停留后再消失,这种表现显得不自然,因此需要进一步调整方案。
分析:
- 现已确认是事件优先级导致的问题,blur优先于click执行,因此需要寻找替代click的方案。
- 我们知道click事件由mousedown和mouseup两个事件组成,且只有当这两个事件发生在同一像素点时才会触发click。即鼠标点击的完整流程为:mousedown -> mouseup -> click。
- 因此,我们编写一个demo来观察具体的事件执行顺序。
methods: {
onClick () {
console.log('click')
},
onMousedown () {
console.log('mousedown')
},
onMouseup () {
console.log('mouseup')
},
onBlur () {
console.log('blur')
}
}
结果

解决方案与最终效果
最终通过将click事件替换为mousedown事件,成功修复了该问题。
<li
:class="[baseClass + '-list']"
v-for="(item, index) in recommendList"
:key="index"
@mousedown="onSearch(item)"
>{{item}}</li>
最终效果展示

本次踩坑与填坑过程至此结束。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
Vue 3 核心变化解析:Composition API 与响应式系统详解
2026-11-03 21:33:38
viewer.js v1 升级到 v2 需要注意哪些 API 变更
2026-11-03 20:42:00
Vue3 与 React 中怎么封装 hls.js 播放器组件
2026-11-03 11:13:51
Vue.js官网访问慢或打不开怎么办?国内镜像与GitHub备用方案
2026-11-02 09:19:09
利用Axios实现无感知双Token刷新的详细教程
2026-11-01 03:11:12
