Thursday, May 2, 2013

Using jQuery To Filter Data

Fig: Screenshot of the result

When a webpage is designed to display large tables of data, a great amount of consideration should be dedicated to allowing the user to sort through the data in a structured manner. In this article, I will go over one techniques:  filtering.

If a website calls for many records of data to be displayed, in my example 1000 rows, then it
is more than appropriate to offer the user a way to sift through the data. One particularly effective way that has sprung up on the web in recent years as part of
the Web2.0/AJAX movement is filtering. This is also something that Apple pushes heavily in there applications such as iTunes. The goal for us is to allow the user
to type a search query into a standard text input and live filter the table rows below only showing the ones that contain matching text. This is arguably more
advanced then the alternating row styles, but in all reality requires minimal code, due to jQuery’s built-in functionality.
First we will write a generic function that takes a selector and a string of text. This function will then search all elements matching that selector looking for
the string. If it finds the string, it will show the element and apply a class name of visible to the element, otherwise it hide the element. Why are we applying
the class of visible? Well once the items are sorted we will want to run the zebraRows function again, but we need to tell jQuery to ignore the hidden rows, and
the best way I have found to do that is apply a class of visible.
The actual searching is done by the JavaScript function, aptly named, search(). Though due to the way the DOM works, if we don’t employ the jQuery function,
text(), the box will also look at any HTML tags that happen to be in the table row, such as <td>. We
will employ a little more functionality by not just searching for the exact string the user has typed, but rather if any of the words in the query are in a row.
This is ideal because it allows for “lazy searching”, the user isn’t required to remember an exact string but rather just parts of it. The search() function takes
a regular expression as its parameter, and so we must strip all white space from the beginning and end of our query and put “|” characters in between each word to
achieve the OR functionality we desire.
  1. //filter results based on query  
  2. function filter(selector, query) {  
  3.   query =   $.trim(query); //trim white space  
  4.   query = query.replace(/ /gi, '|'); //add OR for regex query  
  6.   $(selector).each(function() {  
  7.     ($(this).text().search(new RegExp(query, "i")) < 0) ? $(this).hide().removeClass('visible') : $(this).show().addClass('visible');  
  8.   });  
  9. }  

The sixth line is where the magic happens, and probably requires a bit of explanation. Starting at line 5, we are telling the code to loop through all the elements
that match the selector, i.e. the rows, and then we want to execute the code on line 6 using each one. Line 6 is a bit complicated if you are new to programming,
but it is fairly easy to grasp if we split it up. Think of everything before the question mark as being a question, if the answer to that question is true then
execute the code to the left of the colon, but after the question mark. If the answer is false then execute the code after the colon. This is essentially an if
statement but in a more concise form known as a ternary operator, and would be no different than writing:

  1. ...  
  2.   if ($(this).text().search(new RegExp(query, "i")) < 0) {  
  3.     $(this).hide().removeClass('visible')  
  4.   } else {  
  5.    $(this).show().addClass('visible');   
  6.   }  
  7. ...  
The reason we ask if search() returns "< 0" is because it returns the position in the string where query is, and -1 if nothing is matched. Because -1 is always
less than zero, we test that condition. In theory there is nothing wrong with checking if it returns (==) -1, but it in practice it is safer to just assure it is
less than zero.
Alright now that we have a complete filter function, let's use jQuery events to hook it up to the input. To achieve the live effect we desire the event we want to
watch for is when user releases a key while they are focused on the text box, known as keyup in JavaScript. It is important that we set the ID attribute of the
input so we can target it using jQuery. Back in our ready function we need to add code after our call to zebraRows().
  1. <label for="filter">Filter</label>  
  2. <input type="text" name="filter" value="" id="filter" />  
And the jQuery code:
  1. ...  
  2.   //default each row to visible  
  3.   $('tbody tr').addClass('visible');  
  5.   $('#filter').keyup(function(event) {  
  6.     //if esc is pressed or nothing is entered  
  7.     if (event.keyCode == 27 || $(this).val() == '') {  
  8.       //if esc is pressed we want to clear the value of search box  
  9.       $(this).val('');  
  11.       //we want each row to be visible because if nothing  
  12.       //is entered then all rows are matched.  
  13.       $('tbody tr').removeClass('visible').show().addClass('visible');  
  14.     }  
  16.     //if there is text, lets filter  
  17.     else {  
  18.       filter('tbody tr', $(this).val());  
  19.     }    

1 comment: