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

Vue.js搜索下拉框focusout阻塞点击事件及代码优化咨询

问题解答

核心bug(点击下拉条目不触发selecteds)修复

问题根本原因是浏览器事件触发时序问题:focusout事件的触发优先级高于click事件,点击下拉条目的时候,输入框先触发失焦,listShow被设为false,下拉列表直接从DOM中移除,绑定在列表条目上的click事件自然不会执行。
修复方案:给inputOutClick的逻辑加100ms左右的延迟,保证点击事件先执行完成:

inputOutClick() {
  setTimeout(() => {
    this.listShow = false
    // 新增无结果时显示newDiv的逻辑
    if (!this.listShow) {
      this.newDiv = true
    }
  }, 120)
}

如果不想用延迟,也可以把下拉条目的@click换成@mousedown,mousedown事件优先级比focusout高,也能解决问题。

两个疑问解答

  • 搜索功能绑定@keyup是否合理?
    基础功能可以正常运行,但存在优化空间:
    1. 要过滤非输入类按键:用户按方向键、Ctrl、Shift这类按键也会触发keyup,不需要发起请求,可以绑定@keyup.enter或者在事件回调里判断event.key过滤无效触发。
    2. 建议加防抖:用户连续输入时会频繁触发请求,加300ms防抖可以大幅减少不必要的接口请求,提升性能。
  • 使用v-on:focusout做失焦处理是否合适?
    本身是合适的,focusout是原生失焦事件,绑定在input上可以准确捕获输入框失去焦点的场景,你遇到的问题只是事件时序的适配问题,不属于事件本身的问题。

代码风格优化建议

你的代码确实有可以精简和规范的点:

  1. 变量名拼写错误:searcList应该修正为searchList,避免后续维护混淆;search方法里this.searcList = this.getList是笔误,应该改为this.searchList = res.data。
  2. 重复的listShow赋值可以简化:多分支判断可以直接简化为一行逻辑this.listShow = !!(this.searchText && res.status === 200 && res.data.length)。
  3. 接口请求的参数拼接建议用模板字符串,可读性更高:把'search' + '?filter=' + this.searchText改成`search?filter=${encodeURIComponent(this.searchText)}`,同时对搜索参数做编码,避免特殊字符导致请求错误。
  4. v-for循环必须绑定唯一key属性,这是Vue的强制规范,避免列表渲染出现异常。

修改后完整代码

JS代码

const app = new Vue({
  el: '#app',
  data: {
    searchText: '',
    listShow: false,
    newDiv: false,
    searchList: [],
    // 防抖定时器标记
    searchTimer: null
  },
  methods: {
    inputOutClick() {
      setTimeout(() => {
        this.listShow = false
        if (!this.listShow) {
          this.newDiv = true
        }
      }, 120)
    },
    selecteds(list) {
      this.listShow = false
      this.searchText = list.name
    },
    async search() {
      // 防抖逻辑
      clearTimeout(this.searchTimer)
      this.searchTimer = setTimeout(async () => {
        if (!this.searchText.trim()) {
          this.listShow = false
          return
        }
        const res = await this.callApi('get', `search?filter=${encodeURIComponent(this.searchText)}`)
        if (res.status === 200) {
          this.searchList = res.data
          this.listShow = !!res.data.length
          // 有结果的话隐藏newDiv
          if (this.listShow) this.newDiv = false
        }
      }, 300)
    }
  }
});

HTML代码

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
  <div>
    <input
      type="text"
      v-model="searchText"
      @keyup="search"
      @focusout="inputOutClick"
    />
    <div v-if="listShow" style="background:red">
      <ul>
        <li v-for="(list, index) in searchList" :key="index">
          <a @click="selecteds(list)">{{ list.name }}</a>
        </li>
      </ul>
    </div>
    <div v-if="newDiv">
      <p>hello</p>
    </div>
  </div>
</div>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 02:51:03