Eric Wendelin's Blog

How to highlight search results with JavaScript and CSS

Google highlight search textYou see it in Google search results and a lot of other sites that have good search functionality. When you perform a search, your words or phrases are highlighted in the search results making it easy for you to find the most relevant content.

Today I’m going to show you a simple way to add this to your website or blog so your users can find what they need in style. I think that this kind of thing should be implemented more often for how easy it is to implement.

Here we go!

Search Results JavaScript code:

function highlightOnLoad() {
  // Get search string
  if (/s\=/.test(window.location.search)) {
    var searchString = getSearchString();
    // Starting node, parent to all nodes you want to search
    var textContainerNode = document.getElementById("content");

    // Informational message for search
    var searchInfo = 'Search Results for: ';

    // Split search terms on '|' and iterate over resulting array
    var searchTerms = searchString.split('|');
    for (var i in searchTerms) 	{
      // The regex is the secret, it prevents text within tag declarations to be affected
      var regex = new RegExp(">([^<]*)?("+searchTerms[i]+")([^>]*)?<","ig");
      highlightTextNodes(textContainerNode, regex, i);
      // Add to info-string
      searchInfo += ' <span class="highlighted term'+i+'">'+searchTerms[i]+'</span> ';
    }

    // Create div describing the search
    var searchTermDiv = document.createElement("H2");
    searchTermDiv.className = 'searchterms';
    searchTermDiv.innerHTML = searchInfo;

    // Insert as very first child in searched node
    textContainerNode.insertBefore(searchTermDiv, textContainerNode.childNodes[0]);
  }
}

// Pull the search string out of the URL
function getSearchString() {
  // Return sanitized search string if it exists
  var rawSearchString = window.location.search.replace(/[a-zA-Z0-9\?\&\=\%\#]+s\=(\w+)(\&.*)?/,"$1");
  // Replace '+' with '|' for regex
  // Also replace '%20' if your cms/blog uses this instead (credit to erlando for adding this)
  return rawSearchString.replace(/\%20|\+/g,"\|");
}

function highlightTextNodes(element, regex, termid) {
  var tempinnerHTML = element.innerHTML;
  // Do regex replace
  // Inject span with class of 'highlighted termX' for google style highlighting
  element.innerHTML = tempinnerHTML.replace(regex,'>$1<span class="highlighted term'+termid+'">$2</span>$3<');
}

// Call this onload, I recommend using the function defined at: http://untruths.org/technology/javascript-windowonload/
addOnLoad(highlightOnLoad());

Now, the highlighting CSS:

span.highlighted {
  background-color: #161616;
  font-weight: bold;
}
span.term0 {
  background-color: #161633;
}
span.term1 {
  background-color: #331616;
}
span.term2 {
  background-color: #163316;
}

Code explanation

First, the highlightOnLoad function checks window.location.search to see if we need to be running any of this stuff, then calls getSearchString to get a sanitized search string so that nothing funky can happen if, say, the user searches for ‘<script>’. You should really be sanitizing all search inputs at least on the back-end anyway.

Then, the highlightTextNodes function uses a regex replace on our textContainerNode’s innerHTML. The regex verifies that the text is between a > and a < (and not the other way around). Actually nice and simple!

Caveats

This may end up being a bit slow if you are doing this on a LOT of text, but for my blog text, it seems quite snappy to me. Also, the CSS does not bold text inside links, but the background color is there to make it obvious.

What do you think? Try it out on the search box on the upper-right. I’m hoping for some optimizations in the comments.

Updates

Reader erlando has recommended some changes that allow for changing the styles of each search term individually and add an informational message showing what the user searched for here are the code updates:

This is added to the end of the highlightOnLoad() function:

  // Informational message for search
  var searchInfo = 'Search Results for: ';

  // Split search terms on '|' and iterate over resulting array
  var searchTerms = searchString.split('|');
  for (var i in searchTerms) 	{
    // The regex is the secret, it prevents text within tag declarations to be affected
    var regex = new RegExp(">([^<]*)?("+searchTerms[i]+")([^>]*)?<","ig");
    highlightTextNodes(textContainerNode, regex, i);
    // Add to info-string
    searchInfo += ' <span class="highlighted term'+i+'">'+searchTerms[i]+'</span> ';
  }

  // Create div describing the search
  var searchTermDiv = document.createElement("H2");
  searchTermDiv.className = 'searchterms';
  searchTermDiv.innerHTML = searchInfo;

  // Insert as very first child in searched node
  textContainerNode.insertBefore(searchTermDiv, textContainerNode.childNodes[0]);

The new highlightTextNodesFunction now takes and additional parameter and uses it to make each search term unique. Here it is:

  function highlightTextNodes(element, regex, termid) {
    var tempinnerHTML = element.innerHTML;
    // Do regex replace
    // Inject span with class of 'highlighted termX' for google style highlighting
    element.innerHTML = tempinnerHTML.replace(regex,'>$1<span class="highlighted term'+termid+'">$2</span>$3<');
  }

Finally we can add as many classes as we want for search terms. Here I am changing the colors slightly.

span.term0 {
  background-color: #161633;
}
span.term1 {
  background-color: #331616;
}
span.term2 {
  background-color: #163316;
}

Now you should try searching my site for: “firecookie extension for firefox” and check out the new improved search! Thanks erlando!!

Popularity: 29% [?]

15 Responses so far

  1. comefeel March 5th, 2008 11:15 pm

    thank .. ^^;

  2. erlando March 12th, 2008 5:23 am

    Just one thing.. If the search contains multiple words in my experience the highlighting becomes erratic.

    I modified the script so it iterates through multiple search terms one at a time:

    var searchTerms = searchString.split(’|');
    for ( var i = 0; i ([^]*)?$1$2$3<’);
    }

  3. erlando March 12th, 2008 5:24 am

    Aaaand my code got eaten.. :-(

  4. Daniel Ervik May 13th, 2008 2:03 pm

    @erlando

    remember to ad !important behind every change. Here is my example:

    @namespace url(http://www.w3.org/1999/xhtml);

    @-moz-document domain(”www.google.com”) {
    span.highlighted {
    background-color: #161616 !important;
    font-weight: bold !important;
    }
    span.term0 {
    background-color: #161633 !important;
    }
    span.term1 {
    background-color: #331616 !important;
    }
    span.term2 {
    background-color: #163316 !important;
    }
    span.term0 {
    background-color: #161633 !important;
    }
    span.term1 {
    background-color: #331616 !important;
    }
    span.term2 {
    background-color: #163316 !important;
    }

  5. Eric Wendelin May 13th, 2008 2:54 pm

    @Daniel:

    I’m sorry if this is a dumb questions but, why would adding !important be uh… important? I know that it overrides any user styles but I try to avoid this when I can.

  6. Mike++ July 18th, 2008 4:20 pm

    Okay javascript is all new for me–but to see this code actually work I should be able to just put it all in the head of an embedded css page, right? I did that and it it didn’t do anything so I don’t know. I could not find any code in there for a search box or any other prompt. please help.
    Thanks

  7. Eric Wendelin July 18th, 2008 10:03 pm

    @Mike++:

    OK so all you need to use this is to copy the javscript code at the top into a <script> tag in the HEAD of our HTML. Replace the last line (addOnLoad…) to
    window.onload = highlightOnLoad;

    Then you need to copy the CSS into a <style type=’text/css’> tag in the HEAD tag as well.

    It should work out. Let me know if you have other problems with it.

Trackbacks

Leave a reply