Skip to content

Instantly share code, notes, and snippets.

@athul
Last active November 7, 2020 14:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save athul/0d47f7e1b677de3f3ed2b756ae1fffac to your computer and use it in GitHub Desktop.
Save athul/0d47f7e1b677de3f3ed2b756ae1fffac to your computer and use it in GitHub Desktop.
fastSearch ported for static html pages in Go

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

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.

Search.js

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']

CSS

#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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment