跳到主要内容

ArtinTech Solution

Shopify FAQ page with a live search box filtering questions on desktop and mobile

Kestrel 的 FAQ 页面越来越长,顾客常常滚过了自己要找的答案。商家给我们看了一个竞争对手的帮助页面,顶部有一个搜索框,并希望我们照着做:输入一个词,只显示相关的问题。

这就是那个 Shopify FAQ 搜索的故事:snippet 本身、让第一版在线上店铺什么也找不到的 bug,以及最终成为一堂关于 Shopify 主题内联脚本课程的修复。

关于本案例:这是根据真实 Shopify 项目整理的综合案例。品牌名称和部分细节已更改。代码、bug 和修复都与实际发生的完全一致。

桌面端和移动端带实时搜索框、可筛选问题的 Shopify FAQ 页面
FAQ 页面随输入实时筛选。(示意图,品牌已匿名。)

为什么不用 Shopify 自带的搜索?

Shopify 店铺搜索覆盖商品、系列、页面和博客文章。商家在主题版块区块里填写的文字,也就是大多数 FAQ 版块存放问题的地方,并不在这个索引里。顾客在店铺里搜索“retour”,找到的是商品,而不是关于退货的答案。

但 FAQ 的答案本来就在页面的 HTML 里。在浏览器中筛选它们,不需要请求、不需要应用、也不需要索引。顾客还在输入,结果就已经出现了。

第一版:一个 snippet 加一行 render

商家在代码编辑器里修改主题,所以我们把改动尽量缩小:一个包含标记、样式和脚本的新 snippet,以及 FAQ 版块中用来渲染它的一行代码。

{%- render 'faq-search', section: section -%}
<div class="faqs ...">  <!-- the theme's existing FAQ markup -->

脚本是一个小型自定义元素。每次输入时,它做四件事:

  • 规范化文本:转为小写并去掉重音符号,这样“e”也能找到“é”。
  • 把搜索词拆成单词,只有标题或答案中包含全部单词的问题才保留。
  • 当某个分类下没有任何匹配的问题时,连同标题隐藏整个分类。
  • 完全没有匹配时,显示“未找到相关问题”的提示,并引导顾客联系客服。

我们在无头浏览器里用一份 FAQ 标记的副本做了测试。每次搜索都返回了正确的问题。于是我们把它交付了。

那张截图

回复里附了一张截图。商家把某个答案里的一整句话粘贴进了搜索框。页面显示“未找到相关问题”,而下面所有问题依然可见。什么都没隐藏,也什么都没找到。

两个现象指向同一件事:脚本以为页面上没有问题。我们打开线上页面,直接查询这个元素。页面上有两组 FAQ、共七个问题,而搜索元素自己的分组列表是空的。

原因:内联脚本执行得太早

snippet 渲染在问题的上方,它的 <script> 标签就在 snippet 里。浏览器从上到下解析 HTML。解析到脚本时,它定义了自定义元素;由于 <faq-search> 已经在文档中,它的 connectedCallback 会立即执行。此时解析器还没有读到下方的问题。

我们的第一版在 connectedCallback 里查找 FAQ 分组并保存起来。结果它保存了一个空列表,而且只保存一次,永远不变。我们的测试之所以通过,是因为测试页面恰好把脚本放在了问题之后。

Shopify 主题编辑器中的代码差异,在 FAQ 搜索 snippet 中用 getter 替换缓存的列表
修复方法:在搜索时查找分组,而不是在元素连接时。

修复只需要一个 getter。脚本不再在元素连接时保存分组,而是在顾客每次输入时重新查找:

get groups() {
  return Array.from(this.closest('.section').querySelectorAll('.faq'));
}

等到有人开始输入时,页面早已解析完毕。对几十个问题来说,这次查找几乎不耗时;而且当商家编辑时主题编辑器重新渲染版块,它也照样有效,缓存的列表就做不到。

第二个更隐蔽的 bug

在把代码移进最终 snippet 时,我们又发现一个问题。负责隐藏被筛掉内容的 CSS 规则写成了 .faq [data-faq-hidden],中间有个空格。它能隐藏分组内的问题,却隐藏不了分组本身,于是可能留下一个下面什么都没有的分类标题。现在这条规则两者都覆盖:.faq [data-faq-hidden], .faq[data-faq-hidden]。

Shopify FAQ 页面搜索“retour”的结果,其中一个答案已展开
修复后:“retour”只显示三个退货问题,其他分类都隐藏了。

在它真正运行的地方测试

这一次,我们直接在线上 FAQ 页面上测试:在浏览器中针对真实标记运行修正后的逻辑,然后才请商家做任何修改。

“levertijd”只显示关于配送时间的问题
“garantie”显示来自两个不同分类的保修问题
“€70 gratis”只显示运费问题,通过答案匹配到
“retour”只显示退货问题,另一个分类被隐藏
“xyz”无结果,并显示“未找到相关问题”

我们的收获

  • 按照真实页面的顺序来测试。在内联脚本中定义的自定义元素,只能看到它上方的 HTML。
  • 除非确定内容已经存在,否则不要在 connectedCallback 中缓存 DOM 查询。getter 比等待 DOMContentLoaded 更简单,也能经受主题编辑器的重新渲染。
  • 商家发来截图时,把几个现象放在一起看。“没有结果”加上“什么都没隐藏”,在打开控制台之前就锁定了问题。

这个搜索后来成为一个完整帮助页面的一部分,详见我们的 Shopify FAQ 页面模板案例。我们为同一家店铺打造的商品页优惠,请看买一送一赠品选择器。如果你的 FAQ 或主题的其他部分也需要这样的工作,请了解我们的 Shopify 主题开发服务。

常见问题

FAQ 搜索框会影响 SEO 吗?

不会。所有问题和答案都保留在 HTML 中,只有在顾客输入搜索词之后,才会在其浏览器中被隐藏。

给 Shopify FAQ 页面加搜索需要应用吗?

不需要。问题本来就在页面上,一个小脚本就能筛选。比应用更快,也没有月费。

FAQ 很多时也能用吗?

能。在浏览器中筛选几百个问题是瞬间完成的。再多的话,把 FAQ 拆分成几个页面通常对顾客更友好。

它也会搜索答案吗?

会。每个问题都会按标题和答案文字进行匹配,忽略重音符号,而且搜索中的所有单词都必须出现。

Share this article