【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
上篇 blog
【Ubuntu】【Hugo】搭建私人博客:搜索引擎
分析了 Fuse.js 初始化 new Fuse(data, options),以及正排索引和倒排索引(现代搜索引擎的核心原理),下面继续
搭建私人博客
OK,接下来看下一部分

这块的整体逻辑,可以实现在用户输入搜索关键词时,实时地在页面上显示匹配的搜索结果(也就是实时搜索和自动补全等功能),下面来详细看下
sInput.onkeyup = function (e) { ... }:sInput 是输入框,之前在 search.html 定义的 searchInput

当用户在输入框中松开键盘按键 onkeyup 时,触发这个函数,每次按键都会执行一次搜索,实现边打字变搜索的体验

fuse 是前面已经初始化好的 Fuse.js 实例,this.value.trim() 表示获取用户当前输入的内容,并去除收尾空格,然后调用 fuse.search(...) 对数据进行模糊匹配,如果用户有自定义配置 params.fuse.Opts.limit,就限制返回结果数量,比如只显示前面 5 条搜素结果

有搜索结果时,就构建 HTML 列表,这里可以看到用 for 循环遍历搜索结果(搜索结果 results 是一个对象数组,每个对象包含 .item 原始数据项),每个结果都会生成一个 <li> 列表项,里面包含标题和指向文章的链接 permalink,搜索结果遍历完成后,将这段动态生成的 HTML 插入到 resList 元素中,resList 就是之前在 search.html 模板里的 searchResults 元素

然后设置标志位 resultsAvailable = true,表示当前有了搜索结果,然后记录第一个和最后一个结果节点,方便后续键盘导航,比如可以用上下箭头选择
另外,这里有个点

就是这里的 <a> 标签是空的,只有 href 和 aria-label,但没有显示内容,一般会把标题,也就是上面的列表项 <li> 放到 <a> 里面,或者让 <header> 包含链接,否则用户可能看不到可点击的链接,必须要做特殊处理
这里的搜索结果生成后,HTML 的列表项结构大致是这样的
html
<li class="post-entry">
<header class="entry-header">标题</header>
<a href="..."></a>
</li>
可以看到 <header> 在 <a> 外面,并没有包含 <a>,<a> 也没有包裹 <header>,它俩是独立的,而因为 <a> 是空的,链接没有承载的地方,那用户就会点击不了,因为 <a> 没有内容(没有文本或子元素),用户就看不到可点击区域,即使鼠标悬停,也看不出这是个链接,点击时只能点到 <header>,但 <header> 不是可点击元素
比如最终效果可能是这样,如果不做特殊处理的话,那 <a> 就会点击不到

这样的话会有几个潜在影响:
- 用户体验差:用户以为能点,但点不中
- 无障碍性差:屏幕阅读器可能读到了
aria-label,但用户完全不知道哪里能点 - 不符合 Web 标准:链接应该有可见内容或明确交互提示
举个标准做法例子:
html
<li>
<a href="/post1" aria-label="文章标题">
<header>文章标题 >></header>
</a>
</li>
或者更简单的:
html
<li>
<a href="/post1">文章标题 >></a>
</li>
这样用户看到的文字直接就是可点击的,鼠标悬停会变形状(表示可点击),屏幕阅读器也能正确识别,也符合 Web 可访问性标准
OK,下面来看 Hugo PaperMod 是怎么特殊处理这部分的,F12 打开搜索界面,可以看到

<header> 区域大小是 421.33 × 57.58 像素大小,而

<a> 区域大小则是 441.33 × 77.58 像素大小,可以看到 PaperMod 主题手动将 <a> 区域的给放大了,所以用户能点击跳转过去
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog