I tweaked my code below from this tutorial. This is for Hugo beginners mostly (like me ).
Before you begin, add this code to your configuration file. I use TOML, so mine looks like this:
[outputs]
home = ["HTML","RSS","JSON"]
And in YAML format:
outputs:
home:
- HTML
- RSS
- JSON
- Create an
index.json
file in the root of layouts folder and add the code below.
[
{{ $post := where site.RegularPages "Type" "post" }}
{{ range $index, $page := $post }}
{{ if $index }},{{ end }}
{
"url": {{ $page.RelPermalink | jsonify }},
"title": {{ $page.Title | jsonify}},
"content": {{ $page.Content | jsonify }}
}
{{ end }}
]
Edit it to fit your needs, e.g changing the $post
parameter or for "content"
, you can use either of these terms. Test if it generates any content by adding /index.json
at the end of your domain/localhost.
- Create a page inside the content folder (in the section containing your pages) to host your search form. I called mine
search.md
. Add this code to it:
---
title: Search Page
url: /search/
_build:
list: never
---
<div class="search-box">
<input class="input" id="searchInput" type="text" placeholder="press '/' to search">
<div id="searchResult">
<!-- the search result will appear here -->
</div>
</div>
- Create a search.js file where you store your static files (usually ‘static’ or ‘assets’ folder), add the code below, then load it in your template’s footer e.g
<script type="text/javascript" src="search.js"></script>
// Begin search.js
let searchInput = document.querySelector('#searchInput'),
searchResult = document.querySelector('#searchResult');
let dataJSON;
// add keydown listener, when user hit '/', it will focus on search input (Desktop)
window.addEventListener('keydown', function(event) {
if (event.key === '/') {
event.preventDefault()
searchInput.focus()
}
})
// add keydown listener, when user hit 'ESC', it will close search result and unfocus search input.
window.addEventListener('keydown', function(event) {
if (event.keyCode === 27)
{
searchInput.value = '';
searchResult.innerHTML = '';
searchInput.blur()
}
})
/**
* Get the posts lists in json format.
*/
const getPostsJSON = async () => {
let response = await fetch('/index.json')
let data = await response.json()
return data
}
/**
* @param query, element.
* query: the keyword that the user gives.
* element: target element to show the result.
*/
const filterPostsJSON = (query, element) => {
let result, itemsWithElement;
query = new RegExp(query, 'ig')
result = dataJSON.filter(item => query.test(item.content))
itemsWithElement = result.map(item => (
`<div class="search-result"><h2><a href="${item.url}">${item.title}</a></h2><p>${item.content}</p></div>`
))
itemsWithElement.unshift(`<p>To cancel search, Press 'ESC'</p>`)
element.innerHTML = itemsWithElement.join('');
}
/**
* searchInputAction take two arguments, event and callback
*/
const searchInputAction = (event, callback) => {
searchInput.addEventListener(event, callback)
}
/**
* When the user focuses on the search input, the function getPostsJSON is called.
*/
searchInputAction('focus', () => getPostsJSON().then(data => dataJSON = data))
/**
* filtering result with the query that user given on search input.
*/
searchInputAction('keyup', (event) => filterPostsJSON(event.target.value, searchResult))
Change the heading h2
here to whatever you like e.g div
<div class="search-result"><h2><a href="${item.url}">${item.title}</a></h2><p>${item.content}</p></div>
You can now test to see if your search form works, then style it to your liking with CSS. Cheers!