Preface
Goal: Javascript Search in Hugo, using lunrjs
1: Prepare
Reading
Source
I respect copyright. The javascript code below, copied and pasted from:
Although both are intented to be used with Jekyll, the Javascript part reamin the same with Hugo.
I also made a slight modification, and a little enhancement. But of course the logic for lunr part remain the same.
Artefacts
This is the list of the Arteftacfts that we require.
Edit Artefact:
-
themes/tutor-06/layouts/partials/site-scripts.html
-
themes/tutor-06/layouts/_default/baseof.html
-
themes/tutor-06/layouts/partials/site-header.html
New Artefact:
-
themes/tutor-06/layouts/search/single.html
-
content/pages/search.md
-
themes/tutor-06/static/js/jquery.min.js
-
themes/tutor-06/static/js/lunr-0.7.1.min.js
-
themes/tutor-06/static/js/search-lunr-0.7.1.js
2: Search Page
The search page consist of this two artefacts:
-
themes/tutor-06/layouts/search/single.html
-
content/pages/search.md
Layout: Single
First we need to create newly layout for search
-
- themes/tutor-06/layouts/search/single.html
- gitlab.com/…/layouts/search/single.html
{{ define "main" }}
<div class="p-2">
<form action="get" id="site_search">
<label for="search_box">Search</label>
<input type="text" id="search_box" name="query">
<input type="submit" value="search">
</form>
<ul id="search_results" class="list-unstyled"></ul>
</div>
{{ end }}
Content: Search
-
- content/pages/search.md
- gitlab.com/…/content/pages/search.md
+++
type = "search"
title = "Search Blog"
+++
Server Output: Browser
Open in your favorite browser. You should see, search form in search page.
- http://localhost:1313/pages/search/
We do not use javascript yet.
3: Navigation Bar
Consider have a look at our ancient artefact, that exist from the very beginning of the tutorial.
We need to update navigation Bar. Change the form part.
-
- themes/tutor-06/layouts/partials/site-header.html
- gitlab.com/…/layouts/partials/site-header.html
<form class="form-inline mt-2 mt-md-0" action="/pages/search/" method="get">
<input class="form-control mr-sm-2" type="text" name="q"
placeholder="Search" aria-label="Search">
<button class="btn btn-outline-light my-2 my-sm-0"
type="submit">Search</button>
</form>
Server Output: Browser
Open in your favorite browser. You should see, search form in navigation bar.
- http://localhost:1313/
What does it do ?
If you type a word to be searched, such as for example time, and click the search button. The page will be redirect to
- http://localhost:1313/pages/search/?q=time
4: Javascript Layout
Riddle ?
We need to forward the url request such as:
- http://localhost:1313/pages/search/?q=time
To the form in search page, using javacript. So the form will show the word time.
We need to put a specific javascript in this serach page.
Layout: baseof
How do we manage javascript for specific page ?
We should have look at our ancient artefact. All we need is to add block.
-
- themes/tutor-06/layouts/_default/baseof.html
- gitlab.com/…/layouts/_default/baseof.html
{{ partial "site-scripts.html" . }}
{{ block "custom-javascript" . }}{{ end }}
The complete code is here
<!DOCTYPE html>
<html lang="en">
{{ partial "site-head.html" . }}
<body>
{{/* partial "service-google-analytics.html" . */}}
{{ partial "site-header.html" . }}
<div class="container-fluid maxwidth">
<div class="row layout-base">
{{- block "main" . }}
{{ .Content }}
{{- end }}
{{- block "aside" . }}{{- end }}
</div><!-- .row -->
</div><!-- .container-fluid -->
{{ partial "site-footer.html" . }}
{{ partial "site-scripts.html" . }}
{{ block "custom-javascript" . }}{{ end }}
</body>
</html>
Layout: Single
And apply the block in layout.
-
- themes/tutor-06/layouts/search/single.html
- gitlab.com/…/layouts/search/single.html
{{ define "main" }}
<div class="p-2">
<form action="get" id="site_search">
<label for="search_box">Search</label>
<input type="text" id="search_box" name="query">
<input type="submit" value="search">
</form>
<ul id="search_results" class="list-unstyled"></ul>
</div>
{{ end }}
{{ define "custom-javascript" }}
<script src="{{ "js/search-form-example.js" | relURL }}"></script>
{{ end }}
Javascript: Example
And the content of the Javascript example is:
-
- themes/tutor-06/static/js/search-form-example.js
- gitlab.com/…/static/js/search-form-example.js
jQuery(function() {
// Get search results if q parameter is set in querystring
if (getParameterByName('q')) {
var query = decodeURIComponent(getParameterByName('q'));
$("#search_box").val(query);
window.data.then(function(loaded_data){
$('#site_search').trigger('submit');
});
}
});
/* ==========================================================================
Helper functions
========================================================================== */
/**
* Gets query string parameter - taken from
* http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
* @param {String} name
* @return {String} parameter value
*/
function getParameterByName(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
Server Output: Browser
Open in your favorite browser. You should see, search form respond to url query.
- http://localhost:1313/pages/search/?q=time
5: JQuery Requirement
Our lunr search require JQuery, instead of just JQuery Slim.
Download JQuery
And put in JS directory under static assets.
- themes/tutor-06/static/js/jquery.min.js
Partial: Javascript
It is just an empty template.
-
- themes/tutor-06/layouts/partials/site-scripts.html
- gitlab.com/…/layouts/partials/scripts.html
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="{{ "js/jquery.min.js" | relURL }}"></script>
<script src="{{ "dist/js/bootstrap.min.js" | relURL }}"></script>
<script src="{{ "js/prism.js" | relURL }}"></script>
6: Search Data Source
Remember our last JSON source ? Consider refresh our last tutorial.
Server Output: Browser
Now you can see JSON output in your favorite browser.
- http://localhost:1313/pages/archives/index.json
This will produce something similar as shown below:
{
"/quotes/2018/09/13/mothers-no-crying-in-baseball/": {
...
},
"/quotes/2018/09/07/julian-baker-something/": {
"title": "Julian Baker - Something",
"content": "I knew I was wasting my time. Keep myself awake at night.",
"url": "http://localhost:1313/quotes/2018/09/07/julian-baker-something/",
"author": "epsi",
"category": "quotes"
},
...
}
Modern browser automatically format, the JSON output in nice view collapsable view.
7: lunr Search
This is the main component.
Download lunrjs.
I haven’t update my skill lately. I’m still using the old lunrjs code.
And put it in JS directory.
- themes/tutor-06/static/js/lunr-0.7.1.min.js
Layout: Single
And apply the block in layout.
-
- themes/tutor-06/layouts/search/single.html
- gitlab.com/…/layouts/search/single.html
{{ define "custom-javascript" }}
<script src="{{ "js/lunr-0.7.1.min.js" | relURL }}"></script>
<script src="{{ "js/search-data-example.js" | relURL }}"></script>
{{ end }}
Javascript: Loading Lunr
And the content of the Javascript example is:
-
- themes/tutor-06/static/js/search-data-example.js
- gitlab.com/…/static/js/search-data-example.js
jQuery(function() {
// Initalize lunr with the fields it will be searching on. I've given title
// a boost of 10 to indicate matches on this field are more important.
window.idx = lunr(function () {
this.ref('id');
this.field('title');
this.field('content', { boost: 10 });
this.field('author');
this.field('category');
});
// Download the data from the JSON file we generated
window.data = $.getJSON('/pages/archives/index.json');
// Wait for the data to load and add it to lunr
window.data.then(function(loaded_data){
$.each(loaded_data, function(index, value){
window.idx.add(
$.extend({ "id": index }, value)
);
});
});
// Event when the form is submitted
$("#site_search").submit(function(event){
event.preventDefault();
var query = $("#search_box").val(); // Get the value for the text field
var results = window.idx.search(query); // Get lunr to perform a search
display_search_results(results); // Hand the results off to be displayed
});
function display_search_results(results) {
console.log(results);
}
});
Notice the data source is that we call
window.data = $.getJSON('/pages/archives/index.json');
Server Output: Browser
Open in your favorite browser. Put any text in search form, and click the search button. You should see, console contain array, as respond to button click.
- http://localhost:1313/pages/search/
In json object, this would be as below:
[
{
"ref": "/quotes/2018/09/07/julian-baker-something/",
"score": 0.1154962866960379
}
]
8: Showing The Result
We are almost finished.
Javascript: Loading Lunr
Showing the result is simply changing the display_search_results.
-
- themes/tutor-06/static/js/search-lunr-0.7.1.js
- gitlab.com/…/static/js/search-lunr-0.7.1.js
function display_search_results(results) {
var $search_results = $("#search_results");
// Wait for data to load
window.data.then(function(loaded_data) {
// Are there any results?
if (results.length) {
$search_results.empty(); // Clear any old results
// Iterate over the results
results.forEach(function(result) {
var item = loaded_data[result.ref];
// Build a snippet of HTML for this result
var appendString = '<li><a href="' + item.url + '">' + item.title + '</a></li>';
// Add it to the results
$search_results.append(appendString);
});
} else {
$search_results.html('<li>No results found</li>');
}
});
}
Layout: Single
Since we change the javascript name again, we must also update the block layout in this artefact.
-
- themes/tutor-06/layouts/search/single.html
- gitlab.com/…/layouts/search/single.html
{{ define "main" }}
<div class="p-2">
<form action="get" id="site_search">
<label for="search_box">Search</label>
<input type="text" id="search_box" name="query">
<input type="submit" value="search">
</form>
<ul id="search_results" class="list-unstyled"></ul>
</div>
{{ end }}
{{ define "custom-javascript" }}
<script src="{{ "js/lunr-0.7.1.min.js" | relURL }}"></script>
<script src="{{ "js/search-lunr-0.7.1.js" | relURL }}"></script>
{{ end }}
Server Output: Browser
Open in your favorite browser. Put any text in search form, and click the search button. You should see, the html result. as text in browser, as respond to button click.
- http://localhost:1313/pages/search/
9: Summary
Now it is about the right time to put it all together.
-
- themes/tutor-06/static/js/search-lunr-0.7.1.js
- gitlab.com/…/static/js/search-lunr-0.7.1.js
// http://rayhightower.com/blog/2016/01/04/how-to-make-lunrjs-jekyll-work-together/
// https://learn.cloudcannon.com/jekyll/jekyll-search-using-lunr-js/
jQuery(function() {
// Initalize lunr with the fields it will be searching on. I've given title
// a boost of 10 to indicate matches on this field are more important.
window.idx = lunr(function () {
this.ref('id');
this.field('title');
this.field('content', { boost: 10 });
this.field('author');
this.field('category');
});
// Download the data from the JSON file we generated
window.data = $.getJSON('/pages/archives/index.json');
// Wait for the data to load and add it to lunr
window.data.then(function(loaded_data){
$.each(loaded_data, function(index, value){
window.idx.add(
$.extend({ "id": index }, value)
);
});
});
function display_search_results(results) {
var $search_results = $("#search_results");
// Wait for data to load
window.data.then(function(loaded_data) {
// Are there any results?
if (results.length) {
$search_results.empty(); // Clear any old results
// Iterate over the results
results.forEach(function(result) {
var item = loaded_data[result.ref];
// Build a snippet of HTML for this result
var appendString = '<li><a href="' + item.url + '">' + item.title + '</a></li>';
// Add it to the results
$search_results.append(appendString);
});
} else {
$search_results.html('<li>No results found</li>');
}
});
}
// Event when the form is submitted
$("#site_search").submit(function(event){
event.preventDefault();
var query = $("#search_box").val(); // Get the value for the text field
var results = window.idx.search(query); // Get lunr to perform a search
display_search_results(results); // Hand the results off to be displayed
});
// Get search results if q parameter is set in querystring
if (getParameterByName('q')) {
var query = decodeURIComponent(getParameterByName('q'));
$("#search_box").val(query);
window.data.then(function(loaded_data){
$('#site_search').trigger('submit');
});
}
});
/* ==========================================================================
Helper functions
========================================================================== */
/**
* Gets query string parameter - taken from
* http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
* @param {String} name
* @return {String} parameter value
*/
function getParameterByName(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
What is Next ?
Feel sleepy ? Although it is a good idea to finish your tutorial. Just like this sleepy cat, you may sleep anytime you want. Have a break for a while, feed your pet, and resume our tutorial.
There is this our last article, how to make your blog site live. This is actually, the first article that I made, before I start the whole tutorial. Consider continue reading [ Git Pages - Deploying ].
Thank you for reading.
Farewell. We shall meet again.