Implementing search on static HTML sites made with Go can be made simple with Fuze.js and an Index file(json). Fork of Craig Mod's fastsearch implementation.
Index.json is an important part for searches here. You need to create an index.json at buildtime or manually. It can have the keys which you wish but please note to change them in the Js file too. As an example, our JSON file will be like
[
{
"title": "Adguard Telegram Bot",
"permalink": "posts/adbot.html",
"tags": [
"go",
"code",
"other"
]
},
{
"title": "Config for Zettel local dev with Air",
"permalink": "posts/air-zettel.html",
"tags": [
"code",
"other"
]
}
]
It will be a JSON array with keys of title
for the title of the post, permalink
for the link and tags
if any for the post. You can change this to any keys of your choice and use it.
You need to install Fuze.js to fully work, you can install from https://fusejs.io/getting-started/installation.html
Once you install Fuze, you can use it in the HTML templates or HTMl files within the <script>
tag. Here is the code,
// Code by Craig Mod under MIT License
// https://gist.github.com/cmod/5410eae147e4318164258742dd053993
var fuse;
var list = document.getElementById('searchResults');
var first = list.firstChild;
var last = list.lastChild;
var maininput = document.getElementById('searchInput');
var resultsAvailable = false;
var baseURL = window.location.pathname.split("/")[1]
if (baseURL.length === 0 || baseURL.includes("html") || baseURL.includes("posts")) {
baseURL = window.location.origin
} else {
baseURL = "/" + baseURL
}
console.log(baseURL)
loadSearch()
document.addEventListener('keydown', function(event) {
if (event.keyCode == 27) {
document.activeElement.blur();
resultsAvailable = false;
list.style.display="none"
}
if (event.keyCode == 40) {
if (resultsAvailable) {
;
event.preventDefault();
if ( document.activeElement == maininput) { first.focus(); }
else if ( document.activeElement == last ) { last.focus(); }
else { document.activeElement.parentElement.nextSibling.firstElementChild.focus(); }
}
}
if (event.keyCode == 38) {
if (resultsAvailable) {
event.preventDefault();
if ( document.activeElement == maininput) { maininput.focus(); }
else if ( document.activeElement == first) { maininput.focus(); }
else { document.activeElement.parentElement.previousSibling.firstElementChild.focus(); }
}
}
});
document.getElementById("searchInput").onkeyup = function(e) {
list.style.display=""
executeSearch(this.value);
}
function fetchJSONFile(path, callback) {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var data = JSON.parse(httpRequest.responseText);
if (callback) callback(data);
}
}
};
httpRequest.open('GET', path);
httpRequest.send();
}
function loadSearch() {
fetchJSONFile(baseURL + "/data/search.json", function(data){
var options = {
shouldSort: true,
location: 0,
distance: 100,
threshold: 0.4,
minMatchCharLength: 2,
keys: ['title','tags']
};
fuse = new Fuse(data, options);
});
}
function executeSearch(term) {
let results = fuse.search(term);
let searchitems = '';
if (results.length === 0) {
resultsAvailable = false;
searchitems = '';
} else {
for (let item in results.slice(0,5)) {
const title = results[item].title.replace(new RegExp(term, "gi"), (match) => `<mark class="searchHgl">${match}</mark>`);
tags = results[item].tags.map((value)=>{
return value.replace(new RegExp(term, "gi"), (match) => `<mark class="searchHgl">${match}</mark>`);
})
searchitems = searchitems + '<li><a href="' + baseURL + '/' + results[item].permalink + '" tabindex="0"><span class="title">' + title + "</span> — " + tags + "</a></li>";
}
resultsAvailable = true;
}
document.getElementById("searchResults").innerHTML = searchitems;
if (results.length > 0) {
first = list.firstChild.firstElementChild;
last = list.lastChild.firstElementChild;
}
}
You have to change the keys of the search according to your index file. This can be updated in keys: ['title','tags']
#fastSearch {
right: 0px;
top: 0px;
display: inline-block;
width: 100%;
}
#fastSearch input {
padding: 4px 10px;
width: 80%;
height: 31px;
font-size: inherit;
font-weight: bold;
border-radius: 3px 3px 0px 0px;
outline: thin;
text-align: left;
}
#searchResults li {
list-style: none;
margin-left: 0em;
background-color: var(--nc-bg-2);
}
#searchResults li .title {
font-size: 1.1em;
margin-bottom: 10px;
display: inline-block;
}
#searchResults {
position: absolute !important;
width: inherit;
left: 0;
}
#searchResults a {
text-decoration: none !important;
padding: 10px;
display: inline-block;
}
#searchResults a:hover,
a:focus {
outline: 0;
color: var(--nc-ac-1);
}
@media screen and (max-width: 900px) {
navbar {
flex-wrap: wrap;
}
.nav-item:last-child {
width: 100%;
text-align: center;
}
#fastSearch input {
width: 100%;
}
#searchResults {
padding: 0;
}
}
.searchHgl {
padding: 0;
background: rgba(121, 40, 202, 0.6);
}