zishu's blog

zishu's blog

一个热爱生活的博主。https://zishu.me

Add a search function to the website using native JavaScript.

This article can be considered as a tutorial on how to add a search functionality to a Hugo website and implement hot reloading for a better user experience.

For other programs, you only need to generate files in the following format according to a specific template. The main code starts from Part 2, and the first chapter explains how to use Hugo to output the article list JSON file.

title is the title of the article, and permalink is the link to the article.

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

1. Generating the Article List JSON File with Hugo Templates#

Create a new file named index.json in the layouts folder, and the template content is as follows:

The "blog" at the end of the second line is the name of your article folder. It is mostly "posts" and so on, but here it is my personal name.

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

With this template, you can open http://localhost:1313/index.json for local preview with Hugo. If some data is output as shown in the figure below, it means you have succeeded.

image

2. JavaScript Code#

Create a new template file named search.html in /layouts/_default, roughly following the structure of other template files, and then write the content we need.

First, we have a simple HTML structure that binds an event to the input.

<form class="search"> 
  <input type="text" id="searchTerm" name="searchTerm" autocomplete="off" oninput="initiateSearch()">
</form>
<div id="resultsContainer">Please enter keywords to search...</div>

Then, we use a GET request to retrieve the JSON file, pass in the search term parameter, and generate the search results list.

<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 = "No search results found.";
      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>

Then, create a search.md file in /content to call this template.

---
slug: search
title: Search
layout: search
---

I have created a basic style that can be used directly.

.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;
}
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.