← home← blog

Creating a dynamic search page for your Jekyll blog using JavaScript

11 Nov 2014

Aim

Provide a sane way for a user to search our static Jekyll blog and view a bookmarkable page containing the relevant results.

For the eager-beavers among you, take a look at the Installation details at the bottom of the post.

Intro

"Transform your plain text into static websites and blogs."

Simply put, that is Jekyll. This post isn't an intro to Jekyll or even a sales pitch for Jekyll. The Jekyll site does a good enough job of that by itself.

This post will, hopefully, provide a needed solution for people (power users?) trying to get more out of Jekyll. Well, more than it was intended for...

Static with a side of dynamic please

Gone are the days when a monolithic heap of PHP was needed to run a simple blog or website. Jekyll came along with a monolithic amount of Ruby but left it on your machine, and not clogging up your webserver. Templating logic, Markdown and HTML go in...a static site comes out. That static site (by "static site", we simply mean HTML, CSS & JavaScript) gets dumped onto your webserver and you're away! All the folder structure is created for you, all the correct links to HTML files, etc...are generated by Jekyll.

Dreamy.

But wait. We kinda liked that little bit of the dynamic-ness that a nifty bit of WordPress PHP could provide.

Oh hi...

	<?php $query = new WP_Query(); ?>

Searching

Searching. A common need for a blog. However, the very concept of "searching" means dynamic results. Query terms that we can not possibly foresee the user providing. Once our site is compiled and sent to the webserver, that's it. No backend logic to handle a search query.

So we have to be smart about this. We'll have to utilise JavaScript, as that's the only real power we have in terms of client-side logic.

We need to get input from the user. We can do this easily enough by outputting a HTML form element in our template, like so:

<form action="undefined/search" method="GET">
	<label for="query">Search:</label>
	<input type="text" name="query" placeholder="Enter your search term">
</form>

This is just a standard HTML form, which when submitted, sends the value of the "query", input in the URL, to the "/search" page.

You can place this anywhere, maybe the header?

Next up, our search.html page.

We need a place to output our search results, which we'll place in our search.html page:

<div id="results">

</div>

JavaScript

You might be wondering why we're outputting an empty div in the HTML. Well, like I said earlier, we can't know what the user will be searching for, so we have to work with what we're given. Which unfortunately, isn't much.

So the idea is, we'll take the search query term from the URL, grab relevant results from a JavaScript array I'm about to show you, and populate our div above with links to the correct posts.

As Jekyll compiles our site for us, it allows us to place special Liquid templating logic in any file, and it'll automatically process them when it parses & compiles your site. Handy.

So we can quite easily create a JavaScript array containing all our site's posts. Again, handy. Place this in your search.html, right below the #results div:



<script>
var JEKYLL_POSTS = [];
{% for post in site.posts %}
	JEKYLL_POSTS.push({
		title: "{{ post.title }}",
		link: "{{ post.url | prepend: site.baseurl }}",
		content: "{{ post.content | strip_newlines | strip_html }}"
	});
{% endfor %}
</script>



Apart from the fact that we're generating JavaScript from Liquid templating through Ruby (gross), it allows us to create quite a sane structure to work with. We now have a JavaScript array which contains an object for each of our site's posts.

Next up, we need to instantiate our JavaScript to actually perform the search on the above array.

Place this below the script tag above:

<script src="undefined/js/search.js"></script>
<script>
	new jekyllSearch({
		selector: "#results",
		properties: ["title", "content"],
		noResultsMessage: "Sorry, no results were found"
	});
</script>

Configuration

The file search.js is JavaScript code you'll be able to drop in to your Jekyll project and not really have to amend at all (unless you're confident you know what you're doing of course!).

We tell the search plugin where to put our results with the selector option. We can then also tell it which parts of the post to search in, ie. you may want to limit searching to the post titles or, let the user search post content as well. If you don't provide this properties option, the plugin will only search the "title" property of each post.

We can also pass in a custom message for when there were no results found from the user's search.

Voilà

Now when the user submits the search form, they are taken to a search page, which will list the relevant results and let them click-through to the posts!

A splash of dynamic-ness for your static Jekyll site.

The output from the plugin (for each search result) looks like this:

<div class="search-result">
	<h2>
		<a href="/jekyll-search-demo/jekyll/update/2014/11/11/php-for-winners.html">PHP for winners</a>
	</h2>
</div>

Installation details

I've set up a sample demo of this functionality, which can be viewed at online.

The code for the demo (and the code you'll need for your site) can be found on GitHub.

The important files

_includes/header.html - This is where we output the search form

search.html - This is where we output the posts and include the JavaScript

js/search.js - This is the search JavaScript plugin itself.

<strong>Notes</strong>
<p>The JavaScript plugin will work out of the box.</p>

<em>However...</em>
<p>You may want to customise the HTML output. You can do this by amending the JavaScript code, in-particular the "outputResults" function which creates the HTML from the found results.</p>

Good things

We now have a bookmarkable, dynamic search page and users can search our static content. Yay JavaScript!

Undesirable things

It relies on JavaScript being enabled. I'll let your conscience deal with that one. The page also isn't SEO-able, as the results are generated by JavaScript on page load.

With a small blog, the amount of data we'll be embedding into search.html will be minor and nothing to worry about.

However

Once your blog starts to grow, you may be concerned with the amount of data that's compiled. My advice would be to drop the content key from the compilation. If you wanted to do that, your new loop would look like this:



<script>
var JEKYLL_POSTS = [];
{% for post in site.posts %}
	JEKYLL_POSTS.push({
		title: "{{ post.title }}",
		link: "{{ post.url | prepend: site.baseurl }}"
	});
{% endfor %}
</script>



You'd then end up with a lightweight data structure for searching. Obviously this means that we then can't use the content for searching purposes, so if you do pass in content in the properties argument for the plugin, it'll simply be ignored.

To clarify, I'd really only resort to doing this if you noticed the perceived page load speed decrease significantly. This would be a premature optimisation if employed too early.


I'm available for hire

Hire me