WP DEVELOP

EP064. “输入防抖与 setTimeout 倒数”

首页 WordPress 开发课程 LIVE SEARCH AJAX · EP064
约 11 分钟· #EP064#LIVE SEARCH AJAX
🔒 登录后可标记已读

开始响应「在搜索框里打字」这个事件。目标是不要用户每按一个字母就立刻发请求(比如打「math」的第一个字母 M 就去查数据库没意义),而是等用户停止打字大约 2000 毫秒之后才真正执行查询逻辑——也就是「防抖」(debounce)的思路。这一讲先用 setTimeout/clearTimeout 把这套「等待 + 重新计时」的机制搭起来,用一条 console.log 占位测试,还没有真的发请求或渲染结果,这些留到下一讲 EP065。

涉及文件

  • wp-content/themes/fictional-university-theme/src/modules/Search.js (修改)

代码实现

// wp-content/themes/fictional-university-theme/src/modules/Search.js

import $ from "jquery"

class Search {
  constructor() {
    this.openButton = $(".js-search-trigger")
    this.closeButton = $(".search-overlay__close")
    this.searchOverlay = $(".search-overlay")
    this.searchField = $("#search-term")   // 新增:选取搜索输入框
    this.events()
    this.isOverlayOpen = false
    this.typingTimer                        // 新增:只声明属性,暂不赋值,用来存放计时器
  }

  events() {
    this.openButton.on("click", this.openOverlay.bind(this))
    this.closeButton.on("click", this.closeOverlay.bind(this))
    $(document).on("keydown", this.keyPressDispatcher.bind(this))
    this.searchField.on("keydown", this.typingLogic.bind(this))   // 新增:监听输入框的按键
  }

  // 新增:处理「打字」这件事的防抖逻辑
  typingLogic() {
    clearTimeout(this.typingTimer)
    this.typingTimer = setTimeout(function () {
      console.log("This is a timeout test.")
    }, 2000)
  }

  keyPressDispatcher(e) {
    if (e.keyCode == 83 && !this.isOverlayOpen) {
      this.openOverlay()
    }

    if (e.keyCode == 27 && this.isOverlayOpen) {
      this.closeOverlay()
    }
  }

  openOverlay() {
    this.searchOverlay.addClass("search-overlay--active")
    $("body").addClass("body-no-scroll")
    console.log("our open method just ran!")
    this.isOverlayOpen = true
  }

  closeOverlay() {
    this.searchOverlay.removeClass("search-overlay--active")
    $("body").removeClass("body-no-scroll")
    console.log("our close method just ran!")
    this.isOverlayOpen = false
  }
}

export default Search

要点:

  • this.searchField 选取的是 footer.php 里那个 id="search-term"<input>(overlay 的搜索框),这个 id 在 EP061 写 overlay HTML 时就已经加好了,这一讲直接拿来用。
  • 监听打字用的是这个输入框专属的 keydown 事件(跟 EP063 那个绑在 $(document) 上、管全局快捷键的 keydown 是两条不同的事件绑定,互不冲突)。
  • 防抖的核心写法:
    1. 每次按键先 clearTimeout(this.typingTimer)——不管之前有没有在倒数,先取消掉。
    2. 再重新 this.typingTimer = setTimeout(fn, 2000)——重新开始一个 2000 毫秒的倒数。
    3. 这样只要用户持续在打字(两次按键间隔小于 2000ms),前一个倒数会不断被取消重来,只有真正停顿超过 2000ms 才会让 setTimeout 里的函数执行一次。
    4. this.typingTimer 必须是 constructor 里声明好的属性(而不是方法内部的局部变量),这样每次调用 typingLogic 才能拿到「上一次」设的那个计时器 ID 去清除,不然每次按键都会创建一个全新、互相独立的计时器,最终会全部各自触发一次(transcript 演示过:如果不清理,打「biology」7 个字母最终会触发 7 次回调,完全没达到防抖的目的)。
    5. .bind(this) 依然要加在 typingLogic 上,否则方法内部 this 会指向搜索输入框元素而不是 Search 对象。
  • 目前 setTimeout 里执行的还只是 console.log("This is a timeout test."),是纯粹的占位测试,真正查数据库/渲染结果放到下一讲。

Hook / Function 速查

名称类型用途
setTimeout(fn, ms)JS 内建函数等待指定毫秒数后执行一次 fn,返回一个计时器 ID
clearTimeout(timerId)JS 内建函数取消一个尚未执行的 setTimeout 计时器
this.searchField.on("keydown", handler)jQuery监听某个具体元素(这里是搜索输入框)的按键事件

常见坑

  • 如果不用 this.typingTimer 这种「存在 constructor 属性里」的写法,而是每次都用一个局部变量接 setTimeout 的返回值,clearTimeout 就清不到上一次的计时器,导致每次按键都会各自独立倒数、各自触发一次,完全起不到「等用户打完字再执行」的效果。
  • 忘记 .bind(this) 会导致 typingLogic 方法内部访问 this.typingTimer 等属性时出错或拿到错误的对象。

延伸 / 后续讲座会用到

下一讲 EP065 会把 setTimeout 里的 console.log 换成真正往 overlay 里渲染 HTML(先是占位文字,还没接 WordPress REST API),并加上打字瞬间就出现的 loading 动画。真正连上 WordPress 数据库、返回真实搜索结果的逻辑要到 Section 14 才开始。

Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 13, EP064