子舒的博客

用原生 js 给网站写个搜索功能

本文也算是一篇教程,可以给 hugo 网站加个搜索功能,并且实现热更新,体验感更好。

如果是其他程序,只需要按照特定的模板生成以下格式的文件即可,主要代码从 第 2 部分 开始,第一章节写的是如何用 hugo 输出文章列表 json 文件。

title 是文章标题,permalink 是文章链接。

[{
  "permalink": "",
  "title": ""
}, {
  "permalink": "",
  "title": ""
}]

1. hugo 模板生成文章列表 json 文件

layouts 文件夹下新建 index.json 文件,模板内容如下:

其中第 2 行最后面的 "blog" 是你文章文件夹的名称。大部分是 posts 等等,这里是我个人的名称。

{{- $.Scratch.Set "posts" slice -}}
{{- range where .Site.RegularPages "Type" "blog" -}}
    {{- $.Scratch.Add "posts" (dict "title" .Title "permalink" .Permalink) -}}
{{- end -}}
{{- $.Scratch.Get "posts" | jsonify -}}

按照这个模板,hugo 本地预览可以打开 http://localhost:1313/index.json 查看,如果输出了一些数据如下图,说明你成功了。

2. js 代码

/layouts/_default 新建一个模板文件 search.html,大致的结构参考其他模板文件,然后写入我们需要的内容。

首先是一个简单的 html 结构,给 input 绑定一个事件。

<form class="search"> 
  <input type="text" id="searchTerm" name="searchTerm" autocomplete="off" oninput="initiateSearch()">
</form>
<div id="resultsContainer">请输入关键词进行搜索...</div>

然后通过一个 get 请求获取 json 文件,传入关键词参数,生成搜索列表。

<script>
  function search(jsonData, searchTerm) {
    let results = [];
    for (let i = 0; i < jsonData.length; i++) {
      for (let property in jsonData[i]) {
        if (jsonData[i].hasOwnProperty(property) && jsonData[i][property].toString().indexOf(searchTerm) > -1) {
          results.push(jsonData[i]);
          break;
        }
      }
    }
    return results;
  }

  function displayResults(searchResults) {
    let container = document.getElementById("resultsContainer");
    container.innerHTML = "";
    if (searchResults.length > 0) {
      for (let i = 0; i < searchResults.length; i++) {
        let resultDiv = document.createElement("div");
        let resultTitle = document.createElement("a");
        resultTitle.innerText = searchResults[i].title;
        resultTitle.setAttribute('href', searchResults[i].permalink)
        resultDiv.appendChild(resultTitle);
        container.appendChild(resultDiv);
      }
    } else {
      let noResultsMessage = document.createElement("p");
      noResultsMessage.innerText = "没有找到搜索结果。";
      container.appendChild(noResultsMessage);
    }
  }

  function initiateSearch() {
    let searchTerm = document.getElementById("searchTerm").value;
    let xhr = new XMLHttpRequest();
    xhr.open('GET', '/index.json', true);
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        let jsonData = JSON.parse(xhr.responseText);
        let searchResults = search(jsonData, searchTerm);
        displayResults(searchResults);
      }
    };
    xhr.send();
  }
</script>

然后再在 /content 新建一个 search.md 文件调用该模板即可。

---
slug: search
title: 搜索
layout: search
---

写了一个基础的样式,可以直接使用。

.search {
  width: 100%;
  display: flex;
  align-items: center;
  height: 36px;
}
.search #searchTerm {
  width: 100%;
  height: 100%;
  outline: none;
  border: none;
  padding: 0 15px;
  box-shadow: 1px 2px 10px rgba(0, 0, 0, 0.1);
}

#resultsContainer {
  margin-top: 20px;
}
#resultsContainer div {
  margin-bottom: 10px;
  margin: 0;
}
#resultsContainer div a {
  display: block;
  width: 100%;
  padding: 6px 10px;
  transition: all 0.1s linear;
  border-radius: 4px;
}
#resultsContainer div a:hover {
  background: #f3f3f3;
}

  • LinuxDo
  • Giscus
  • Twikoo
昵称
邮箱
网址
0/500
  • OωO
  • |´・ω・)ノ
  • ヾ(≧∇≦*)ゝ
  • (☆ω☆)
  • (╯‵□′)╯︵┴─┴
  •  ̄﹃ ̄
  • (/ω\)
  • ∠( ᐛ 」∠)_
  • (๑•̀ㅁ•́ฅ)
  • →_→
  • ୧(๑•̀⌄•́๑)૭
  • ٩(ˊᗜˋ*)و
  • (ノ°ο°)ノ
  • (´இ皿இ`)
  • ⌇●﹏●⌇
  • (ฅ´ω`ฅ)
  • (╯°A°)╯︵○○○
  • φ( ̄∇ ̄o)
  • ヾ(´・ ・`。)ノ"
  • ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
  • (ó﹏ò。)
  • Σ(っ °Д °;)っ
  • ( ,,´・ω・)ノ"(´っω・`。)
  • ╮(╯▽╰)╭
  • o(*////▽////*)q
  • >﹏<
  • ( ๑´•ω•) "(ㆆᴗㆆ)
  • 😂
  • 😀
  • 😅
  • 😊
  • 🙂
  • 🙃
  • 😌
  • 😍
  • 😘
  • 😜
  • 😝
  • 😏
  • 😒
  • 🙄
  • 😳
  • 😡
  • 😔
  • 😫
  • 😱
  • 😭
  • 💩
  • 👻
  • 🙌
  • 🖕
  • 👍
  • 👫
  • 👬
  • 👭
  • 🌚
  • 🌝
  • 🙈
  • 💊
  • 😶
  • 🙏
  • 🍦
  • 🍉
  • 😣
  • 颜文字
  • Emoji
  • Bilibili
1 条评论
哈哈

别有忧愁暗恨生,此时无声胜有声

子舒. Some rights reserved.

Using for Hugo.