Lightweight jquery search/filter tutorial

A tutorial for creating a lightweight jQuery search/filter for lists or on-page elements with CSS transitions.

jqeury search/filter

In this tutorial we will explore using basic jQuery to use it to enable instant searching/filtering of an unordered list. Take a look at the demo below and see what we will be building!

Demo

For this tutorial, we are going to create a simple list. This can be a list of whatever you want. Here, I have created a list of delicious dessert foods—may you now become hungrier and hungrier! The goal is to allow the user to search through this list using keywords. We want the list to respond instantly and dynamically. To keep things fast, we will rely on CSS for animation and use jQuery only for the logic.

Note that this tutorial requires jQuery version 1.10 or higher.

The basic HTML

First things first: the HTML. We need a list and a search field, so let's do that first. Populate it with whatever you want. Dessert, entrees, side-dishes, your favorite Star Trek:TNG characters.

<input type="text" id="search-text" placeholder="search">
<ul id="list">
  <li>Apple pie</li>
  <li>Pumpkin pie</li>
  <li>Banana-creme pie</li>
  <li>Peach-blackberry cobbler</li>
  <li>Chocolate-strawberry torte</li>
  <li>Chocolate-zucchini cake</li>
  <li>Anything involving chocolate and mint</li>
  <li>Red-velvet cake</li>
  <li>Anything involving fruits that aren't cherries</li>
</ul>

With that, we have the basics of everything we need for the search functionality. Let's move on to the jQuery so this thing can start working.

The basic jQuery

What we are trying to accomplish is to allow the user to type in the search box and immediately filter away any items that do not match the search. Because we want this to be a little bit flexible, we are not going to require case sensitivity (i.e. the user can type in all lower case) and we are not going to try to match exact strings (i.e. typing "banana pie" will find "banana-creme pie"). In order to filter the list, we will simply add a CSS class that can be used to toggle the display. Here is the basic jQuery to get everything working—take a look and read through the comments.


$(document).ready(function() {

  //we want this function to fire whenever the user types in the search-box
  $("#search-text").keyup(function () {
  
    //first we create a variable for the value from the search-box
    var searchTerm = $("#search-text").val();

    //then a variable for the list-items (to keep things clean)
    var listItem = $('#list').children('li');
    
    //extends the default :contains functionality to be case insensitive
    //if you want case sensitive search, just remove this next chunk
    $.extend($.expr[':'], {
      'containsi': function(elem, i, match, array)
      {
        return (elem.textContent || elem.innerText || '').toLowerCase()
        .indexOf((match[3] || "").toLowerCase()) >= 0;
      }
    });//end of case insensitive chunk


    //this part is optional
    //here we are replacing the spaces with another :contains
    //what this does is to make the search less exact by searching all words and not full strings
    var searchSplit = searchTerm.replace(/ /g, "'):containsi('")
    
    
    //here is the meat. We are searching the list based on the search terms
    $("#list li").not(":containsi('" + searchSplit + "')").each(function(e)   {

          //add a "hidden" class that will remove the item from the list
          $(this).addClass('hidden');

    });
    
    //this does the opposite -- brings items back into view
    $("#list li:containsi('" + searchSplit + "')").each(function(e) {

          //remove the hidden class (reintroduce the item to the list)
          $(this).removeClass('hidden');

    });
    

As you are looking through, you may ask why we are bothering to add a class rather than simply use jQuery's .hide and .show functions? You certainly can do so, but by using CSS classes instead, you have a lot more flexibility with what you want to do with your "hidden" class. Sure, you can use jQuery animations, but these are far slower than using CSS. Read here for reasons why.

The basic CSS

From here you just need a bit of CSS and everything will be up and running.


.hidden {
  display:none;
}

That is it. Now you have a basic, functioning search/filter. But clearly in the demo we are doing a bit more than just setting the list item to display:none;, right?

Getting a bit fancy

I am separating this next part because it is not necessary for your search/filter to function. If you just needs something that works, feel free to run-off and get to work. But to make things look a bit nicer, take a look below.

jquery search results

In initially building this, I first thought of using CSS3 animations to give some nice style to the removing of the list items. This is great and I would love it if that would fit what I needed to build, but the lack of browser support for CSS animations meant this wasn't the best route. Having used them before in demos and prototypes, I know there can be unexpected results in unsupported browsers. If this is a route you can take, go for it! For help with doing so, read this great tutorial.

For our purposes, though, we want to use plain old CSS transitions. These deprecate nicely and have far greater browser support. But there is no CSS transition between display:block; and display:none;, so we have to introduce something else.

With these transitions we have a number of different options. What I decided upon was to transition the margin-left and the opacity. But I also want to remove the items from the flow with display:none;. Let's take another stab at the jQuery—read through the comments below to see what is going on.


    $("#list li").not(":containsi('" + searchSplit + "')").each(function(e)   {

      //instead of setting display:none; via the "hidden" class first, we will specify a new class
      //the "hiding" class we can use to introduce transitions
      $(this).addClass('hiding out').removeClass('in');

      //we also want to remove the item from the flow, but not until the transition is finished
      //so we create a setTimeout function to add the "hidden" class after 300 milliseconds
      setTimeout(function() {
          $('.out').addClass('hidden');
        }, 300); //the time here should reflect the duration of your transitions

    });
    
    $("#list li:containsi('" + searchSplit + "')").each(function(e) {

      //to bring it back into view we first introduce it back into the flow
      $(this).removeClass('hidden out').addClass('in');

      //then we remove the transition class to have it slide back into view
      //we need a slight delay in order for the transitions to occur
      setTimeout(function() {
          $('.in').removeClass('hiding');
        }, 1);
    });


   /*
    * we have also introduced the "in" and "out" classes
    * these are used for keeping track of what is being shown and what is being hidden
    */

With that we use a setTimeout function in order to apply a transition and remove the element from the page-flow. Now we just need some extra CSS to apply the transition.


/* first, some transitions on the list elements (don't forget to browser-prefix here) */
li {
  transition: margin .4s ease-in-out;
}

.hiding {
  margin-left:-100%;
  opacity:0.5;
}

And that is it! Now we have an animated list. Of course, there are many different transitions you can add to this—experiment and see which you like best!

Some finishing touches

jquery search no results

As you can see in the demo, I have added a few extras to my list. A results count to the right and a "no results" message for an empty list. Take a look at the final HTML below to see the two small additions for both of these extras.


  <input type="text" id="search-text" placeholder="search" class="search-box">
  <span class="list-count"></span><!--here we have a placeholder for the list count-->
    
    
<ul id="list">
  <li class="in">Apple pie</li>
  <li class="in">Pumpkin pie</li>
  <li class="in">Banana-creme pie</li>
  <li class="in">Peach-blackberry cobbler</li>
  <li class="in">Chocolate-strawberry torte</li>
  <li class="in">Chocolate-zucchini cake</li>
  <li class="in">Anything involving chocolate and mint</li>
  <li class="in">Red-velvet cake</li>
  <li class="in">Anything involving fruits that aren't cherries</li>
  <span class="empty-item">no results</span><!--and here an "empty list" notice-->
</ul>

For both of these items, we need a bit more jQuery. First, the list-count. This snippet needs to be placed in two separate places: once just within document.ready() so that it will count the list items before the user starts typing and once within our keyUp function so that it counts the items as the user types.


 //placed twice within our jQuery
 var jobCount = $('#list .in').length;
  $('.list-count').text(jobCount + ' items');

Next, for our "no results" message, we have a tiny bit of jQuery to add. This will follow our jobCount function and use this same variable to determine whether or not to show the message. You should place this just below the second jobCount snippet and within the keyUp function (we only need to fire this once the user has started searching, because we will always begin with items on the page).


    //check to see if the count is zero, if so, add the "empty" class to the list
    if(jobCount == '0') {
      $('#list').addClass('empty');
    }
    //otherwise, remove the "empty" class
    else {
      $('#list').removeClass('empty');
    }

Now we just need a little CSS to tell the "no results" message when it needs to be visible. Because it is always on the bottom of the list, I have no removed it from the document flow with display:none; and will simply hide it's visibility with CSS. Take a look at the CSS below.


.empty-item {
  /* add more styles here to make it look as you want it to */
  visibility:hidden;
  opacity:0;
}

/* only show the item when it's wrapper is listed as empty */
.empty .empty-item {
  opacity:1;
  visibility:visible;
}


/* now for the transitions, as always, don't forget browser extensions */


/* we want no transition to hide the message, so that it makes way for incoming list items */
.empty-item {
  transition-property: opacity;
  transition-duration: 0s;
  transition-delay: 0s;
  transition-timing-function: ease;
}

/* we do want a transition as it appears, however */
.empty .empty-item {
  transition-property: opacity;
  transition-duration: .2s;
  transition-delay: .3s; 
  /* here we add a transition delay, so that it doesn't appear until the last list item has 
     transitioned off of the page */
  transition-timing-function: ease;
}

The end

Now we are done! Take a look through the final code over on codepen if you are still confused about where things go—just click on "edit this pen". Leave questions or comments below and let me know what you think or what improvements can be made. Or fork the pen and post it below! As noted earlier, if there is any interest in seeing this developed as a simple jQuery plugin, let me know!

Happy coding.

25 thoughts on “Lightweight jquery search/filter tutorial

    • Sadly I don’t have a great deal of experience working with MySQL databases (particularly not interacting with them with jQuery). From a quick search, it seems like you can do so. To adapt this to search a database, it depends on your use case. You would likely have to use AJAX to integrate this. If the database is reasonably small, you could load the data initially and create an array out of it and use the jQuery to search the array.

      This, though, is intended to be a simple solution for searching within a page, rather than connecting to the back-end. If you get a back-end search working, post a link here! Would love to see it.

      • Yes, you can use it with MySQL and PHP.
        Just replace this:

        Apple pie
        Pumpkin pie

        Anything involving fruits that aren’t cherries

        with something like:

        <?php
        mysql_connect("localhost", "user", "password");
        mysql_query("SET NAMES 'utf8'");
        mysql_select_db("databse");
        $data = mysql_query("SELECT `dbfield` FROM `table` ");
        while ($field = mysql_fetch_object($data)) {
        echo "$field->dbfield”;
        }
        ?>

  • Rob,
    I will preface this with comment with- I am a newbie to html/css/js. I love this concept. I am trying to implement on my site. I am having a couple of problems.

    First, When I use only the js,css,and html files from codepen it does not work correctly. I searched the source code and there is a very large that is needed for the search/filter to work properly. I added that in and now the search/filter works…am I missing something? You did not mention it in the above tutorial.

    I am currently trying to change the transitions, but I am not having luck changing the settings in the css file. I want to have the list hidden until the typing begins, then have the list appear from the bottom of the screen… sort of dissolving up.

    does that make sense?

  • Hi,
    Thanks for this simple yet awesome tutorial.

    The only problem i encountered is as soon as i add an space (after a letter or a word) the current results vanishes and only comes back after i add another letter.
    EX: For the search term “web” i get the results i need, but as soon as i add an space “web ” the results goes away… is there any way to fix it.. so the current result stays even if i add a space and only re-filter if i add any letter/word after the space., Hope it makes sense.

    Thanks for the tutorial.

    P.S : In your demo it works perfectly.. even with spaces but in mine it doesnt.. is it because you have added/used auto complete function in your demo?

    • Awesome that you are using the tutorial! Did you include the “searchSplit” variable declaration?

      var searchSplit = searchTerm.replace(/ /g, "'):containsi('")

      Using that replaces space characters so that they are not included in the search parameters. If this isn’t part of your issue, let me know! Do you have a link to your code so I can see the problem?

  • Turns out it wasn’t working because i was using jQuery v1.9.1 and it started working perfectly with jQuery 1.10.2

    And yes i was using searchSplit.., Thanks again.

    • Woah this is really cool!

      Though, I have encountered an error when I hit ‘backspace’ and try to enter a new set of terms. The list doesn’t revert back to it’s initial state but the list count says there are no results.

      • Glad you like it! Double check that you are using a newer version of jQuery — this issue has popped up before for those using an older version on their page. It requires v1.10 or higher. If that isn’t the issue, post back here (with a link, if possible) and I will do what I can to help troubleshoot!

  • This is awesome and exactly what I was looking for. I am having some issues getting the items to reappear when you blank out the field (searching for “”). I see yours brings them all back – do you know what line of code may be causing that?

    Thanks for the help!

    • awsome! my search is now working perfectly.. i forgot to add jquery link.. i have a question, can we put accordion at each menu ? can u pls help me. ;)

      • Glad you got it to work! Often it is just a simple missing link or something that breaks everything :) You should be able to easily put whatever you want in each menu item. Here is a link to a simple content expander that I built recently, but have yet to write up a tutorial for, if you want to take a look at the code: http://cdpn.io/taKHv

        I would be happy to help you implement something, though! Feel free to shoot me an email at: hello(at)robsawyer.me

  • Hello,

    it’s great !! but I have a question .. I would like to show the search result only when I typed..

    how can I do?

    Thank’s

  • Hi, how to use this plugin if i have on page for example 4 or more with id #list, i have on page 4 with id #list, and work just first

  • Hey there! Really nice tutorial! I also really like the finished product a lot! (btw, I found you on codepen. I searched for ‘search’ and your project there came up.)

    Anyway, I’m trying to make this with pure JavaScript.
    I found a JavaScript way of doing ‘extend’ here: http://stackoverflow.com/questions/11197247/javascript-equivalent-of-jquerys-extend-method
    I then got stuck for the rest of the code. I’m not talking about the animation part. Just the bare minimum. How can I convert the following to JavaScript?

    //extends the default :contains functionality to be case insensitive
    //if you want case sensitive search, just remove this next chunk
    $.extend($.expr[':'], {
    ‘containsi’: function(elem, i, match, array)
    {
    return (elem.textContent || elem.innerText || ”).toLowerCase()
    .indexOf((match[3] || “”).toLowerCase()) >= 0;
    }
    });//end of case insensitive chunk

    //this part is optional
    //here we are replacing the spaces with another :contains
    //what this does is to make the search less exact by searching all words and not full strings
    var searchSplit = searchTerm.replace(/ /g, “‘):containsi(‘”)

    //here is the meat. We are searching the list based on the search terms
    $(“#list li”).not(“:containsi(‘” + searchSplit + “‘)”).each(function(e) {

    //add a “hidden” class that will remove the item from the list
    $(this).addClass(‘hidden’);

    });

    //this does the opposite — brings items back into view
    $(“#list li:containsi(‘” + searchSplit + “‘)”).each(function(e) {

    //remove the hidden class (reintroduce the item to the list)
    $(this).removeClass(‘hidden’);

    });

  • Thanks, extremely lightweight as you write but I find that your demo example works better than your documented code.

    I also re-factorized your #id’s and .classes to variables so that you only need to change the var $(“# or .”) from one spot.

    Great work !

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>