Adding full-text search to static HTML websites

A while back I posted about converting my blog to a static HTML site. I described how I used Wintersmith. At the end I mentioned that I would write up another post on how I integrated search into my static site. This is that post.

Now, search functionality in any site usually involves a back-end server to return search results. Or, you can use something like the Google search api. However, I don't like using this because it takes you to Google's results pages, and they throw up ads on it. So, after some searching, I found a really cool free service called Tapirgo. This service lets you submit an RSS or Atom feed url, and then they give you back an API key to make search requests.

It is fairly limited feature wise, as in, you don't actually create an account, you just give them a feed link and email address and you're ready to go. Then, what they do is ping your feed every 15 minutes for new content and index it. You can then run full-text search queries using their API and get back JSON results. Take a look at their site for more info. Their homepage gets right to the nitty gritty.

So, let me show you how I did it:

First, I do not have a back-end server to make API calls to Tapirgo and then render the results in a response. Second, I do not want to do any page manipulation with Javascript when someone runs a search. My site uses very little Javascript, (I only have a very small library for code syntax highlighting) and I wanted to keep it that way. That means, no jQuery, no DOM manipulation libraries, or anything else like that. I opted to make a static page with a URL of "/search". Then, when this page loads, I can fire off a JSONP call and drop some HTML results on the page with basic vanilla JS.

I don't want to use something like jQuery or another Ajax library just for a simple JSONP call, and all that really is, is just an injection of a script tag in your header that the browser evaluates. So, a little simple code that can drop a script tag in the header with the API call is all we need. Create a new file called search.js and put this code right at the top.

var query = getParameterByName("s");

if (query !== "") {
    var script = document.createElement("script");
    script.setAttribute('type', 'text/javascript');
    script.setAttribute("src", "http://www.tapirgo.com/api/1/search.json?token=[API KEY]&query=" + query + "&callback=onSearchResults");

    var head = document.getElementsByTagName('head').item(0);
    head.appendChild(script);
}

So, this is fairly simple. What it does is gets the query from the querystring calling a method called getParameterByName. Then it creates a script element, sets the type to text/javascript and sets the src attribute to the Tapirgo API call. Don't forget to insert your API key. Now, just grab the head element and append the script tag. That way the browser will execute it once it gets a response back.

Now, Tapirgo has a really cool feature where you can pass along a callback parameter in your API call. Then when they send the response back, they wrap it in a function call with the name you passed as the callback value. So, when the browser executes the javascript response in the script tag, it is basically calling a method of your choice with the JSON response as an argument. Great idea on their part to add this feature!

When our response comes back, it will call the callback method we specified called onSearchResults. Let's add a method in our file like so.

function onSearchResults(results) {
    var resultsOutput = "";

    if (results.length > 0) {
        for (var id in results) {
            resultsOutput += '<div class="result"><h3><a href="' + results[id].link + '">' + results[id].title + '</a></h3><p class="date">' + results[id].published_on.split('T')[0] + '</p><p>' + results[id].summary.substr(0, 200) + '...</p></div>';
        }
    } else {
        resultsOutput += "<p>No results found.</p>";
    }

    var el = document.querySelector("#search_results");
    el.innerHTML = resultsOutput;
}

This loops over each result entry in the results array and builds up an HTML string that we can then drop onto the page. If there is no items in the results array, it puts a "No Results found." message instead. I kept it pretty simple here. Nothing fancy, just a quick and dirty render method. It could probably be optimized quite a bit, but I don't anticipate the need for it anytime soon! I also have a div on my page with an id of search_results. This is where it drops the HTML of the search results.

That's pretty much it. This code just needs to go at the end of your page right before the closing body tag. That way, the HTML elements it needs on the page will already be in place. If you put it in the head of your page, it will throw errors when trying to access the elements it needs.

But, I forgot one thing! There is a method I am calling called getParameterByName. This parses out the query string and gets the search terms the user supplied. Luckily, we don't need much here. Nothing a quick Google search couldn't take care of. Here is a snippet of code I snagged off of Stack Overflow. I apologize to the original author. I did not take a note of who posted it. Add this to the end of your file.

function getParameterByName(name) {
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
    var results = regex.exec(location.search);

    return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

This runs a regex to extract the query parameter name and value that you pass into it. Then if it finds a value, it returns the captured value from the regex, or it returns a blank string. If you don't understand what a regex is, or what this method is actually doing, I wouldn't worry too much about it. Just know that it works and does what it needs to do.

So, now you're all set. The only thing left to do it add a search form somewhere on your pages. Point it to this new search page, and make sure that the name of your input box of the search form is s, or matches whatever you pass into the getParameterName method.

I hope this was helpful. Now you can add search functionality into your own static html site. Enjoy!