Super-easy filterable lists/tables with jQuery

I’m working on a page that will potentially have a very long table of information, and I wanted a way to filter the table to only show rows that contain a specific text string.

Fortunately, with jQuery that’s super easy. It took me about 3 minutes to build and test. Let’s take a look!

First, you want to create your table. Give it a class you’ll be able to use to tell jQuery this is what you’re filtering. Something like this:

<table class="filterable">
  <tbody>
    <tr>…</tr>
  </tbody>
</table>

Now let’s put in a little form field for entering the filter string. This is old news now, but HTML5 lets us use one-off form inputs for on-page actions without having to wrap them in a <form> tag, so let’s just stick this into our HTML above the table:

<div>
  Filter: <input type="text" id="list_filter" />
</div>

Now here’s where it gets fun. In jQuery, we’re going to watch for the keyup event on the input. If the input has a value (i.e. is not empty), we’ll do our filtering. If it is empty, we’ll just reveal all of the rows in the table again.

We probably want the filter to be case-insensitive, so we’ll make both the input string and our check of each row’s text all-lowercase with .toLowerCase().

Next we’ll step through each row of the table, check if our filter string is not present (.indexOf() == -1) in the .text() inside that row. If it’s not there, we’ll hide the row. Otherwise, we’ll show it. (This last bit is important because we want to start revealing previously hidden rows if the user deletes characters from the filter input.)

Yeah… that’s pretty much it. And since it’s all jQuery interacting with elements already present on the page, it’s lightning-fast.

<script>
  jQuery(function() {
    jQuery('input#list_filter').on('keyup', function() {
      if (jQuery(this).val() != '') {
        var filter_val = jQuery(this).val().toLowerCase();
        jQuery('table.filterable tbody tr').each(function() {
          if (jQuery(this).text().toLowerCase().indexOf(filter_val) == -1) {
            jQuery(this).hide();
          }
          else {
            jQuery(this).show();
          }
        });
      }
      else {
        jQuery('table.filterable tbody tr').show();
      }
    });
  });
</script>

A few other notes:

  1. The way this is built, you could conceivably have multiple filterable tables on one page, but only one filter input. The filter would automatically get applied to all filterable tables on the same page. There are various ways of changing this by modifying your selector.
  2. I am deliberately only checking for <tr> tags inside the <tbody> tag to allow for a header row inside a <thead> tag that would not be subject to the filter. If you have a header row inside your <tbody> tag, it’s going to get filtered too! Probably not desirable.
  3. You could play around with making the whole thing smoother with .slideUp() and .slideDown() instead of .hide() and .show() but that UX can get messy in a hurry.