Adam Laycock

IT Engineer, Developer & Blogger

Setting up lunr.js in Jekyll

lunrjs is a client side search system that is perfectly suited for Jekyll and other static site systems.

Setting it up in Jekyll without any plugins (so it works on Github Pages) isn’t that hard only needs a couple of extra files and some Javascript. You can see the commit used to add lunr to this site here.

First off I needed a JSON file with all the data I want to make searchable in, I created /content.json which is generated by this:

1{
2 {% for entry in site.posts %}
3 "{{ entry.url | slugify }}": {
4 "title": "{{ entry.title | xml_escape }}",
5 "url": "{{ entry.url | xml_escape }}",
6 "summary": "{{ entry.content | strip_html | strip_newlines | truncatewords: 50 | xml_escape }}"
7 },
8 {% endfor %}
9 {% for entry in site.pages %}
10 "{{ entry.url | slugify }}": {
11 "title": "{{ entry.title | xml_escape }}",
12 "url": "{{ entry.url | xml_escape }}.html",
13 "summary": "{{ entry.content | strip_html | strip_newlines | truncatewords: 50 | xml_escape }}"
14 }
15 {% unless forloop.last %},{% endunless %}
16 {% endfor %}
17}
Language json

I had issues getting the content safe for JSON, mainly with gists adding html etc… it got messy and I ended up truncating the number of words to 50 hoping that I wouldn’t have any special content in my posts in the first 50 words.

With lunr added to the template I added loadSearch() to the documents ready event to load search after the page has loaded.

1function loadSearch(){
2 // Create a new Index
3 idx = lunr(function(){
4 this.field('id')
5 this.field('title', { boost: 10 })
6 this.field('summary')
7 })
8
9 // Send a request to get the content json file
10 $.getJSON('/content.json', function(data){
11
12 // Put the data into the window global so it can be used later
13 window.searchData = data
14
15 // Loop through each entry and add it to the index
16 $.each(data, function(index, entry){
17 idx.add($.extend({"id": index}, entry))
18 })
19 })
20
21 // When search is pressed on the menu toggle the search box
22 $('#search').on('click', function(){
23 $('.searchForm').toggleClass('show')
24 })
25
26 // When the search form is submitted
27 $('#searchForm').on('submit', function(e){
28 // Stop the default action
29 e.preventDefault()
30
31 // Find the results from lunr
32 results = idx.search($('#searchField').val())
33
34 // Empty #content and put a list in for the results
35 $('#content').html('<h1>Search Results (' + results.length + ')</h1>')
36 $('#content').append('<ul id="searchResults"></ul>')
37
38 // Loop through results
39 $.each(results, function(index, result){
40 // Get the entry from the window global
41 entry = window.searchData[result.ref]
42
43 // Append the entry to the list.
44 $('#searchResults').append('<li><a href="' + entry.url + '">' + entry.title + '</li>')
45 })
46 })
47}
Language js

A fair bit of that is unique to this site but the principle is the same for any site. On page load a new index is created and populate when the request for /content.json completes. Binding a couple of events to make the UI work how I wanted and to handle a user making a search.

The search is pretty simple the lunr index has a search method which you pass the search string to. It returns an array of results which I loop though and append to a list.

The order is set by the relevance of the result with the closer matches at the top.

This is a great feature which should help users find content easier.

Adam Laycock

Adam Laycock

IT Engineer, Developer & Blogger

All content is my own unless otherwise stated.

My content is licensed under the CC-BY-NC-SA 4.0 license