How to insert the search box on your Jekyll site using lunr.js in Shopify
Drive 20-40% of your revenue with Avada
In order for users to be able to find relevant content easily, you can actively add search to your store site. It is a relatively small site. Therefore, you are allowed to perform the search entirely client side. In case there were so many blog posts, customers may need to wait for a long time to search the post. Hence, a backend search could be a good choice.
However, please keep reading our instructional writing on How to insert the search box on your Jekyll site using lunr.js to know more deeply about this topic.
How to insert the search box on your Jekyll site using lunr.js
What is Lunr.js?
Lunr.js could be an excellent choice for you if you want to build the search. However, first, what is Lunr.js?
Lunr.js is known as a small, full-text library for use in the browser. It indexes JSON documents and brings along a simple search interface for retrieving documents that can match text queries the most. The search usually works in the following steps:
- Get the search term sent as a GET parameter to
search.html
. search.html
then reads the GET parameter and searches through JSON containing the searchable content.search.html
shows a search result list.
To get started, we will generate /js/search.js
to hold our search JavaScript and get junr.js installed to /js/lunr.min.js
.
How to insert the search box on your Jekyll site using lunr.js?
Step 1: Create search.html
In the first step, we need to generate /search.html
. This file contains a search box, a placeholder for showing results, a JSON output of all the content we want to search on. All JavaScript libraries are also included:
---
layout: search
---
<form action="/search.html" method="get">
<label for="search-box">Search</label>
<input type="text" id="search-box" name="query">
<input type="submit" value="search">
</form>
<ul id="search-results"></ul>
<script>
window.store = {
{% for post in site.posts %}
"{{ post.url | slugify }}": {
"title": "{{ post.title | xml_escape }}",
"author": "{{ post.author | xml_escape }}",
"category": "{{ post.category | xml_escape }}",
"content": {{ post.content | strip_html | strip_newlines | jsonify }},
"url": "{{ post.url | xml_escape }}"
}
{% unless forloop.last %},{% endunless %}
{% endfor %}
};
</script>
<script src="/js/lunr.min.js"></script>
<script src="/js/search.js"></script>
Step 2: Write JavaScript
Move to the next step, to have /js/search.js
perform 3 tasks, we need to write the JavaScript. Those three tasks include:
- Get the search term
- Perform the search
- Display the results
We are going over the sections of code. Then, we will see the whole file at the end.
Step 3: Get the search term
It is not really easy for JavaScript to instantly read GET parameters so we need to add a getParameterByName
function to get things done. Please do not be worried if you don’t get how this function works, it is merely manipulating the query string to divide it into variables.
Now you are able to employ getParameterByName
to get the search term:
...
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (pair[0] === variable) {
return decodeURIComponent(pair[1].replace(/\+/g, '%20'));
}
}
}
var searchTerm = getQueryVariable('query');
...
Step 4: Perform the search
Just in case there is a search term, we need to set up and adjust lunr.js. This job involves telling Lunr about the fields that interest you and adding the search data from the JSON. You can perform the search once this is already set up:
...
if (searchTerm) {
document.getElementById('search-box').setAttribute("value", searchTerm);
// 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.
var idx = lunr(function () {
this.field('id');
this.field('title', { boost: 10 });
this.field('author');
this.field('category');
this.field('content');
});
for (var key in window.store) { // Add the data to lunr
idx.add({
'id': key,
'title': window.store[key].title,
'author': window.store[key].author,
'category': window.store[key].category,
'content': window.store[key].content
});
var results = idx.search(searchTerm); // Get lunr to perform a search
displaySearchResults(results, window.store); // We'll write this in the next section
}
}
...
Step 5: Display the result
Now you are allowed to show the results in your list when already having them.
...
function displaySearchResults(results, store) {
var searchResults = document.getElementById('search-results');
if (results.length) { // Are there any results?
var appendString = '';
for (var i = 0; i < results.length; i++) { // Iterate over the results
var item = store[results[i].ref];
appendString += '<li><a href="' + item.url + '"><h3>' + item.title + '</h3></a>';
appendString += '<p>' + item.content.substring(0, 150) + '...</p></li>';
}
searchResults.innerHTML = appendString;
} else {
searchResults.innerHTML = '<li>No results found</li>';
}
}
...
When putting it all together, you will have a working search:
(function() {
function displaySearchResults(results, store) {
var searchResults = document.getElementById('search-results');
if (results.length) { // Are there any results?
var appendString = '';
for (var i = 0; i < results.length; i++) { // Iterate over the results
var item = store[results[i].ref];
appendString += '<li><a href="' + item.url + '"><h3>' + item.title + '</h3></a>';
appendString += '<p>' + item.content.substring(0, 150) + '...</p></li>';
}
searchResults.innerHTML = appendString;
} else {
searchResults.innerHTML = '<li>No results found</li>';
}
}
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (pair[0] === variable) {
return decodeURIComponent(pair[1].replace(/\+/g, '%20'));
}
}
}
var searchTerm = getQueryVariable('query');
if (searchTerm) {
document.getElementById('search-box').setAttribute("value", searchTerm);
// 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.
var idx = lunr(function () {
this.field('id');
this.field('title', { boost: 10 });
this.field('author');
this.field('category');
this.field('content');
});
for (var key in window.store) { // Add the data to lunr
idx.add({
'id': key,
'title': window.store[key].title,
'author': window.store[key].author,
'category': window.store[key].category,
'content': window.store[key].content
});
var results = idx.search(searchTerm); // Get lunr to perform a search
displaySearchResults(results, window.store); // We'll write this in the next section
}
}
})();
Step 6: Search field
Now only by adding a form that submits to /search.html
can you be able to insert a search box any places on your store site. You will need to prefix the action with `` when you are using a base url on GitHub pages.
...
<form action="/search.html" method="get">
<label for="search-box">Search</label>
<input type="text" id="search-box" name="query">
<input type="submit" value="search">
</form>
...
Conclusion
This solution is also approachable to your blog posts, you can do the same thing for data files, collections, or static files.