My web site is using Hugo based on a collection of posts written in markdown.
The content of my site is technical notes. I am keeping some tags to filter down my searches, but I am often in need of searching keywords through my posts.
For instance I can remember I used FFASM to test some Biztalk pipeline. I want to be able to use that keyword to find the right posts.
I have basic/good know ledge of css, html and go.
Javascript is out of my confort zone.
Steps Overview
1 generate an index file in json to be used as source for the searches
2 update the navbar to incluse a search bar
3 get a script to perform the search
4 wire some UI components with the seatch script
Updating the Configuration File
Open the config.toml file and add the follwoing entries :
When the site is build, the index file is available at the following url : (http://localhost:1313/index.json)[http://localhost:1313/index.json]
Navbar customisation
In my partial nav component I have appended a form in my Navigation bar with an input box and a button. I kept the 2 checkbox to enable the search and to enforce regex search but they are hidden.
They could be removed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<divclass="bd-search"id="docsearch"data-bd-docs-version="5.2"style="margin-left: auto;"><formclass="d-flex"role="search"><inputid="search"class="form-control me-2"type="text"placeholder="press '/' to search"aria-label="Search"><buttonid="searchBtn"class="btn btn-outline-secondary"type="button"data-bs-toggle="modal"style="margin-left: 1em;"data-bs-target="#searchModal">Search</button><divclass="form-check d-none"><inputclass="form-check-input"type="checkbox"value=""id="enable_search"checked><labelclass="form-check-label"for="enable_search">Search</label></div><divclass="form-check d-none"><inputclass="form-check-input"type="checkbox"value=""id="regex_mode"checked><labelclass="form-check-label"for="regex_mode">Regex</label></div></form></div>
Important names are the id for the objects : “search”, “searchBtn”, “enable_search” and “regex_mode”. Those will be used later in the search script.
Bootstrap Modal Layout
The result of the search will be shown in a modal window. When the search engine is running, the output will be set inside the “ul” component with the id “listOfUrl”.
The search script is reading the json search index, looking for matching entries.
It is inspired from (zwbetz)[https://zwbetz.com/build-a-search-bar-for-your-hugo-blog-with-a-json-index-and-some-vanilla-js/] example.
Create a search.js script in the “assets” folder and add the script below :
(function(){constSEARCH_ID='search';constENABLE_SEARCH_ID='enable_search';constREGEX_MODE_ID='regex_mode';constCOUNT_ID='count';constLIST_ID='listOfUrl';letlist=null;letfilteredList=null;constlogPerformance=(work,startTime,endTime)=>{constduration=(endTime-startTime).toFixed(2);console.log(`${work} took ${duration} ms`);};constgetSearchEl=()=>document.getElementById(SEARCH_ID);constgetEnableSearchEl=()=>document.getElementById(ENABLE_SEARCH_ID);constgetRegexModeEl=()=>document.getElementById(REGEX_MODE_ID);constgetCountEl=()=>document.getElementById(COUNT_ID);constgetListEl=()=>document.getElementById(LIST_ID);constdisableSearchEl=placeholder=>{getSearchEl().disabled=true;getSearchEl().placeholder=placeholder;};constenableSearchEl=()=>{getSearchEl().disabled=false;getSearchEl().placeholder='Case-insensitivesearchbytitle,content,orpublishdate';};constdisableRegexModeEl=()=>{getRegexModeEl().disabled=true;};constenableRegexModeEl=()=>{getRegexModeEl().disabled=false;};constfetchJsonIndex=()=>{conststartTime=performance.now();disableSearchEl('Loading...');consturl=`${window.location.origin}/index.json`;fetch(url).then(response=>response.json()).then(data=>{list=data.post;filteredList=data.post;enableSearchEl();logPerformance('fetchJsonIndex',startTime,performance.now());}).catch(error=>console.error(`Failed to fetch JSON index: ${error.message}`));};constfilterList=regexMode=>{console.log("filterList");constregexQuery=newRegExp(getSearchEl().value,'i');constquery=getSearchEl().value.toUpperCase();filteredList=list.filter(item=>{consttitle=item.Title.toUpperCase();constcontent=item.PlainContent.toUpperCase();if(regexMode){return(regexQuery.test(title)||regexQuery.test(content));}else{return(title.includes(query)||content.includes(query));}});};constrenderCount=()=>{constcount=`Count: ${filteredList.length}`;getCountEl().textContent=count;};constrenderList=()=>{constnewList=document.createElement('ul');newList.id=LIST_ID;newList.className="list-group";filteredList.forEach(item=>{constli=document.createElement('li');consttitleLink=document.createElement('a');titleLink.href=item.RelPermalink;titleLink.textContent=item.Title;li.className="list-group-item";li.appendChild(titleLink);newList.appendChild(li);});constoldList=getListEl();oldList.replaceWith(newList);};consthandleSearchEvent=()=>{conststartTime=performance.now();constregexMode=getRegexModeEl().checked;filterList(regexMode);renderCount();renderList();logPerformance('handleSearchEvent',startTime,performance.now());};consthandleEnableSearchEvent=()=>{if(getEnableSearchEl().checked){fetchJsonIndex();enableRegexModeEl();}else{disableSearchEl('Disabled...');disableRegexModeEl();}};constaddEventListeners=()=>{getEnableSearchEl().addEventListener('change',handleEnableSearchEvent);getSearchEl().addEventListener('keydown',handleSearchEvent);getRegexModeEl().addEventListener('change',handleSearchEvent);handleEnableSearchEvent();};constmain=()=>{if(getSearchEl()){addEventListeners();}};main();})();
Updating the Site Footer
In my partial footer component, I have added the following scripts:
{{partial"bootstrap-js.html".}}{{ifsite.Params.search}}{{$searchJs:=resources.Get"search.js"|resources.ExecuteAsTemplate"search.js".|fingerprint}}<scriptsrc="{{ $searchJs.RelPermalink }}"></script><script>$(document).ready(function(){// Show the Modal on load
$("#searchBtn").click(function(){$("#searchModal").modal("show");});// Hide the Modal
$("#hideBtn").click(function(){$("#searchModal").modal("hide");});});</script>{{end}}
I mentioned the bootstrap-js.html in the script because it is referencing the jquery script that is required to run the search.