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 Index3 idx = lunr(function(){4 this.field('id')5 this.field('title', { boost: 10 })6 this.field('summary')7 })89 // Send a request to get the content json file10 $.getJSON('/content.json', function(data){1112 // Put the data into the window global so it can be used later13 window.searchData = data1415 // Loop through each entry and add it to the index16 $.each(data, function(index, entry){17 idx.add($.extend({"id": index}, entry))18 })19 })2021 // When search is pressed on the menu toggle the search box22 $('#search').on('click', function(){23 $('.searchForm').toggleClass('show')24 })2526 // When the search form is submitted27 $('#searchForm').on('submit', function(e){28 // Stop the default action29 e.preventDefault()3031 // Find the results from lunr32 results = idx.search($('#searchField').val())3334 // Empty #content and put a list in for the results35 $('#content').html('<h1>Search Results (' + results.length + ')</h1>')36 $('#content').append('<ul id="searchResults"></ul>')3738 // Loop through results39 $.each(results, function(index, result){40 // Get the entry from the window global41 entry = window.searchData[result.ref]4243 // 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.