The search form code (add it to /layouts/partials/
folder as search-form.html
). Filter the $pages
variable to suit your needs (e.g. by type, by section, by date, reverse, etc.).
Summary
{{ $pages := where site.RegularPages "Section" "==" "blog" }}
{{ $format := "2006-01-02"}}
<input
class="form-control"
id="search"
type="text"
aria-label="Search by title"
placeholder="Loading..."
disabled
>
<p id="count">
<span id="count-label">
<strong>Count:</strong>
</span>
<span id="count-value">
{{ len $pages }}
</span>
</p>
<div id="list" class="">
{{ range $pages }}
<p>
<span class="post-date">
{{ .PublishDate.Format $format }}
</span>
<br>
<span class="post-title">
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
</span>
</p>
{{ end }}
</div>
</div>
The JavasCript Code (save the file as search.js
in assets/js/
folder)
Summary
(function () {
const LOG_ENABLED = false;
const logObj = (objNameStr, obj) => {
if (LOG_ENABLED) {
console.log(objNameStr, JSON.stringify(obj));
}
};
const logPerformance = (funcNameStr, func) => {
const startTimeNum = performance.now();
func();
const endTimeNum = performance.now();
const durationStr = (endTimeNum - startTimeNum).toFixed(2);
if (LOG_ENABLED) {
console.log(`${funcNameStr} took ${durationStr} ms`);
}
};
const getSearchEl = () => {
return document.querySelector('#search');
};
const getCountEl = () => {
return document.querySelector('#count-value');
};
const getPostEls = () => {
return document.querySelectorAll('#list p');
};
const getQueryWordsArr = () => {
return getSearchEl().value.trim().toUpperCase().split(' ');
};
const getPostTitleStr = postEl => {
return postEl
.querySelector('span.post-title')
.textContent.trim()
.toUpperCase();
};
const isHit = (queryWordsArr, titleStr) => {
return queryWordsArr.every(queryWordStr => {
return titleStr.includes(queryWordStr);
});
};
const showPost = postEl => {
postEl.style.display = 'block';
};
const hidePost = postEl => {
postEl.style.display = 'none';
};
const updateCountEl = countNum => {
getCountEl().textContent = countNum;
};
const filterPosts = () => {
const queryWordsArr = getQueryWordsArr();
const postEls = getPostEls();
let countNum = postEls.length;
postEls.forEach(postEl => {
const titleStr = getPostTitleStr(postEl);
const hit = isHit(queryWordsArr, titleStr);
logObj('queryWordsArr', queryWordsArr);
logObj('titleStr', titleStr);
if (hit) {
showPost(postEl);
} else {
hidePost(postEl);
countNum--;
}
});
updateCountEl(countNum);
};
const handleKeyupEvent = () => {
logPerformance('filterPosts', filterPosts);
};
const enableSearchEl = () => {
getSearchEl().disabled = false;
getSearchEl().placeholder = 'Search by title';
};
const main = () => {
getSearchEl().addEventListener('keyup', handleKeyupEvent);
enableSearchEl();
};
main();
})();
Create a search.html
layout for your search page and store it in layouts/_default
. An example
Summary
{{ define "main"}}
<header>
<h1>{{ .Title }}</h1>
</header>
<article>
{{ partial "search-form.html" . }}
</article>
{{ end }}
Create a search.md
page in your content folder and include your layout. An example
Summary
---
title: Search Page
url: /search/
layout: search
---
Call the script in your baseof.html
file before the closing </body>
tag. An example
Summary
{{- if hasPrefix .RelPermalink "/search/"}}
{{- $search := resources.Get "js/search.js" }}
<script src="{{ $search.RelPermalink }}" defer></script>
{{- end }}
There are other ways to achieve this. So, feel free to adapt this to your needs.
Credit: This solution is a modified version originally by @zwbetz I found here. See it in action.