EP070. “搜索 Overlay 的几个体验细节优化”
🔒 登录后可标记已读篇幅比较短,做四件跟 REST API 本身没关系的体验优化:把原本写死在 footer.php 里的 overlay HTML 搬到 Search.js 用 JS 动态插入(这样禁用 JS 的访客不会白白多下载这段永远打不开的 HTML);把打字停顿等待时间从 2000 毫秒缩短到 750 毫秒;overlay 打开后自动把光标定位到搜索框(但要等 CSS 淡入动画播完,否则很多浏览器 focus 不会生效);以及每次关闭 overlay 再重新打开时自动清空上次搜索的内容。
涉及文件
wp-content/themes/fictional-university-theme/footer.php(修改,删掉写死的search-overlayHTML 区块)wp-content/themes/fictional-university-theme/src/modules/Search.js(修改,新增addSearchHTML()方法,调整typingTimer的等待毫秒数,openOverlay()新增清空输入框 + 延迟 focus 的逻辑)
代码实现
footer.php 删掉这一整块(原本在 </footer> 和 <?php wp_footer(); ?> 之间):
<!-- footer.php:删掉,搬去 Search.js 里用 JS 动态生成 -->
<div class="search-overlay">
<div class="search-overlay__top">
<div class="container">
<i class="fa fa-search search-overlay__icon" aria-hidden="true"></i>
<input type="text" class="search-term" placeholder="What are you looking for?" id="search-term">
<i class="fa fa-window-close search-overlay__close" aria-hidden="true"></i>
</div>
</div>
<div class="container">
<div id="search-overlay__results"></div>
</div>
</div>
Search.js 改动:
// wp-content/themes/fictional-university-theme/src/modules/Search.js
class Search {
constructor() {
// 新增:必须放在最前面,因为下面几行要选取的元素(#search-term 等)
// 这时候才刚被 addSearchHTML() 插入到 DOM 里
this.addSearchHTML()
this.resultsDiv = $("#search-overlay__results")
this.openButton = $(".js-search-trigger")
this.closeButton = $(".search-overlay__close")
this.searchOverlay = $(".search-overlay")
this.searchField = $("#search-term")
this.events()
this.isOverlayOpen = false
this.isSpinnerVisible = false
this.previousValue
this.typingTimer
}
// ...events() 不变...
typingLogic() {
if (this.searchField.val() != this.previousValue) {
clearTimeout(this.typingTimer)
if (this.searchField.val()) {
if (!this.isSpinnerVisible) {
this.resultsDiv.html('<div class="spinner-loader"></div>')
this.isSpinnerVisible = true
}
// 修改:等待时间从 2000 缩短到 750 毫秒
this.typingTimer = setTimeout(this.getResults.bind(this), 750)
} else {
this.resultsDiv.html("")
this.isSpinnerVisible = false
}
}
this.previousValue = this.searchField.val()
}
// ...getResults() 不变(还是 EP069 那版)...
openOverlay() {
this.searchOverlay.addClass("search-overlay--active")
$("body").addClass("body-no-scroll")
// 新增:每次打开都先清空上一次搜索的内容
this.searchField.val("")
// 新增:等 301 毫秒(CSS 淡入过渡刚好跑完)再 focus,
// 立刻 focus 在很多浏览器里因为元素还没被视为"可见"而不会生效
setTimeout(() => this.searchField.focus(), 301)
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
}
// 新增:把原本写死在 footer.php 里的 overlay HTML 搬过来,用 jQuery append 到 body 最底部
addSearchHTML() {
$("body").append(`
<div class="search-overlay">
<div class="search-overlay__top">
<div class="container">
<i class="fa fa-search search-overlay__icon" aria-hidden="true"></i>
<input type="text" class="search-term" placeholder="What are you looking for?" id="search-term">
<i class="fa fa-window-close search-overlay__close" aria-hidden="true"></i>
</div>
</div>
<div class="container">
<div id="search-overlay__results"></div>
</div>
</div>
`)
}
}
要点:
addSearchHTML()必须在构造函数最开头就调用,顺序反了的话,this.resultsDiv = $("#search-overlay__results")这些选择器这时候在 DOM 里根本还找不到对应元素(因为 HTML 还没插进去),会拿到空的 jQuery 对象。$("body").append(\...\)用的也是模板字符串反引号,方便把整段 HTML 保持多行缩进书写。setTimeout(() => this.searchField.focus(), 301):这里的箭头函数同样是为了让this继续指向Search对象,而不是被setTimeout劫持。- 清空输入框用的是
this.searchField.val(""),跟读取输入框内容的this.searchField.val()(不传参数)是同一个 jQuery 方法,传空字符串就是"设置"用法。
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
$(selector).append(html) | jQuery 方法 | 把一段 HTML 插入到被选中元素内容的最后面 |
setTimeout(fn, ms) | JS 内建 function | 等待指定毫秒数后执行一次某个函数 |
$(selector).val() / $(selector).val("") | jQuery 方法 | 不传参数时读取表单元素当前值;传字符串参数时是设置该值 |
$(selector).focus() | jQuery 方法 | 让某个表单元素获得输入焦点(光标定位过去) |
常见坑
- overlay 的显示/隐藏是靠 CSS class 切换配合过渡动画(大约 300 毫秒淡入淡出),如果
openOverlay()里的类名加上去之后立刻同步调用.focus(),很多浏览器(包括讲师演示用的这版 Chrome)会因为元素此时还没被认为是"可见"而 focus 失败,必须用setTimeout等过渡跑完(比如 301 毫秒)再 focus。 addSearchHTML()如果没有放在构造函数最前面执行,后面几行选取#search-overlay__results、#search-term等元素的代码会因为 DOM 里还没有这些元素而选空。
延伸 / 后续讲座会用到
下一讲 EP071 开始学习让搜索同时覆盖多种文章类型(posts + pages),并在 EP072 用 $.when().then() 重构成并行请求。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 14, EP070