Small change to Client side searching for Hugo.io with Fuse.js to allow the search to proceed dynamically as the user types the query.
Last active
February 8, 2022 21:23
-
-
Save fooqri/a0d94853f658fb4a4bfee2ac00ab6177 to your computer and use it in GitHub Desktop.
dynamic client side searching for Hugo.io with Fuse.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{{ define "main" }} | |
<section class="resume-section p-3 p-lg-5 d-flex flex-column"> | |
<div class="my-auto" > | |
<form action="{{ "search" | absURL }}" onkeyup="executeInlineSearch()"> | |
<input id="search-query" placeholder="Search..." name="s" autocomplete="off" search autofocus/> | |
</form> | |
<div id="search-results"> | |
<h3 class="search-results-title">Matching pages</h3> | |
</div> | |
</div> | |
</section> | |
<!-- this template is sucked in by search.js and appended to the search-results div above. So editing here will adjust style --> | |
<script id="search-result-template" type="text/x-js-template"> | |
<div class="search-results-summary" id="summary-${key}"> | |
<h4><a class="search-item" href="${link}">${title}</a></h4> | |
<p>${snippet} ... <a class="search-item-more" href="${link}">[more]</a></p> | |
${ isset tags }<p>${tags}</p>${ end } | |
${ isset categories }<p>Categories: ${categories}</p>${ end } | |
</div> | |
</script> | |
{{ end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
summaryInclude=60; | |
var fuseOptions = { | |
shouldSort: true, | |
includeMatches: true, | |
threshold: 0.0, | |
tokenize:true, | |
location: 0, | |
distance: 100, | |
maxPatternLength: 32, | |
minMatchCharLength: 1, | |
keys: [ | |
{name:"title",weight:0.8}, | |
{name:"contents",weight:0.5}, | |
{name:"tags",weight:0.3}, | |
{name:"categories",weight:0.3} | |
] | |
}; | |
var searchQuery = param("s"); | |
if(searchQuery){ | |
// searchQuery = $("#search-query").val(searchQuery); | |
var inputBox = document.getElementById('search-query'); | |
inputBox.value = searchQuery || ""; | |
executeSearch(searchQuery, false); | |
}else { | |
$('#search-results').append("<p class=\"search-results-empty\">Please enter a word or phrase above, or see <a href=\"/tags/\">all tags</a>.</p>"); | |
} | |
function executeInlineSearch(){ | |
$(".search-results-empty").remove(); | |
$(".search-results-summary").remove(); | |
// $('#search-results') | |
var query = document.getElementById("search-query").value; | |
if(query){ | |
executeSearch(query, true); | |
}else { | |
$('#search-results').append("<p class=\"search-results-empty\">Please enter a word or phrase above, or see <a href=\"/tags/\">all tags</a>.</p>"); | |
} | |
} | |
function executeSearch(searchQuery, clear_list){ | |
$.getJSON( "/index.json", function( data ) { | |
var pages = data; | |
var fuse = new Fuse(pages, fuseOptions); | |
var result = fuse.search(searchQuery); | |
if(result.length > 0){ | |
populateResults(result); | |
}else{ | |
if (clear_list) { | |
$(".search-results-empty").remove(); | |
} | |
$('#search-results').append("<p class=\"search-results-empty\">No matches found</p>"); | |
} | |
}); | |
} | |
function populateResults(result){ | |
searchQuery = document.getElementById("search-query").value; | |
$.each(result,function(key,value){ | |
var contents= value.item.contents; | |
var snippet = ""; | |
var snippetHighlights=[]; | |
var tags =[]; | |
if( fuseOptions.tokenize ){ | |
snippetHighlights.push(searchQuery); | |
}else{ | |
$.each(value.matches,function(matchKey,mvalue){ | |
if(mvalue.key == "tags" || mvalue.key == "categories" ){ | |
snippetHighlights.push(mvalue.value); | |
}else if(mvalue.key == "contents"){ | |
start = mvalue.indices[0][0]-summaryInclude>0?mvalue.indices[0][0]-summaryInclude:0; | |
end = mvalue.indices[0][1]+summaryInclude<contents.length?mvalue.indices[0][1]+summaryInclude:contents.length; | |
snippet += contents.substring(start,end); | |
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0],mvalue.indices[0][1]-mvalue.indices[0][0]+1)); | |
} | |
}); | |
} | |
if(snippet.length<1){ | |
snippet += contents.substring(0,summaryInclude*2); | |
} | |
//pull template from hugo templarte definition | |
var templateDefinition = $('#search-result-template').html(); | |
//replace values | |
var tags = "" | |
if (value.item.tags){ | |
value.item.tags.forEach(function(element) { | |
tags = tags + "<a href='/tags/"+ element +"'>" + "#" + element + "</a> " | |
}); | |
} | |
var output = render(templateDefinition,{key:key,title:value.item.title,link:value.item.permalink,tags:tags,categories:value.item.categories,snippet:snippet}); | |
$('#search-results').append(output); | |
$.each(snippetHighlights,function(snipkey,snipvalue){ | |
$("#summary-"+key).mark(snipvalue); | |
}); | |
}); | |
} | |
function param(name) { | |
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' '); | |
} | |
function render(templateString, data) { | |
var conditionalMatches,conditionalPattern,copy; | |
conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g; | |
//since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop | |
copy = templateString; | |
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) { | |
if(data[conditionalMatches[1]]){ | |
//valid key, remove conditionals, leave contents. | |
copy = copy.replace(conditionalMatches[0],conditionalMatches[2]); | |
}else{ | |
//not valid, remove entire section | |
copy = copy.replace(conditionalMatches[0],''); | |
} | |
} | |
templateString = copy; | |
//now any conditionals removed we can do simple substitution | |
var key, find, re; | |
for (key in data) { | |
find = '\\$\\{\\s*' + key + '\\s*\\}'; | |
re = new RegExp(find, 'g'); | |
templateString = templateString.replace(re, data[key]); | |
} | |
return templateString; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for the gist! For some reason, it does not work for my hugo page. Is it still up-to-date? I've seen it still work on your personal blog site.