Checkbox filters with jQuery

Perhaps I’m using delicious.com wrong, but sometimes I wish I had the ability to narrow down my results from a variety of different tags rather than excluding results that don’t share the first tag I marked. That’s where this exercise came into play.

The technique can be used anywhere. In my line of work (online hotel reservations), I’ve experimented with this technique for a room selection for online hotel reservations. For instance, hide all but the non-smoking rooms with a king bed, and only show me rooms that I can pay for in advance to get the cheaper price. That sort of thing.

For this article, I’m going to base my examples on delicious.com’s bookmarks.

Disclaimer stuff

This isn’t so much of a ‘teaching’ post as it is an ‘asking to be taught’ post. Despite the danger I can cause, I’m no JavaScript/jQuery guy, and there are plenty of places here that I’m going about this all wrong. In those instances, I need your help. I’ll call out some specific questions where I know I need improvement. And if you see additional places, please let me know. In the meantime, don’t look to this post as tried-and-true, ready-to-copy-and-paste way to do things, because it’s far from it.

The HTML

Let’s recreate a delicious bookmark listing and leave out the filters for now. Here’s the html for a single listing:

<ul>
  ...
  <li>
    <h4>The Power of WordPress and jQuery: 25+ Useful Plugins & Tutorials</h4>
    <p class="url">http://www.noupe.com/jquery/the-power-of-wordpress-and-jquery-30-useful-plugins-tutorials.html</p>
    <p class="tags"><strong>Tags:</strong> plugin, wordpress, webdesign, jquery</p>
  </li>
  ...
</ul>

In order for this to work, we need a way to get the list items using class names. So the tags are repeated as class names. Also, I’m going to give this particular list a class name so the javascript has something to target later.

<ul class="filterThis">
  ...
  <li class="plugin wordpress webdesign jquery">
    <h4>The Power of WordPress and jQuery: 25+ Useful Plugins & Tutorials</h4>
    <p class="url">http://www.noupe.com/jquery/the-power-of-wordpress-and-jquery-30-useful-plugins-tutorials.html</p>
    <p class="tags"><strong>Tags:</strong> plugin, wordpress, webdesign, jquery</p>
  </li>
  ...
</ul>

We create several of these records and we have ourselves a nice delicious-like page of results.

Let’s say it in English

The JavaScript for this is kind of long and boring, not to mention that I’m not doing it right in several places. So instead of reprinting it, I’d rather just say in English what I wanted the JavaScript to do. Writing out the logic in English is always a good practice anyway. So here goes:

When the page loads, look through all the class names of the list items to be filtered, and build a new list with all the unique values of those class names, along with a corresponding checkbox. Being friendly, we’ll also include a ‘show all’ checkbox at the end of the list. Since all items are shown by default, all checkboxes will be shown by default, too. When someone unchecks a box, hide any item that has the class name of that checkbox’s value. When rechecked, show any item that has the class name of that checkbox’s value.

Mouthful, right? There’s much more to come, as I quickly realized that there is more I needed to do with this list.

Working through one issue at a time

Issues I immediately noticed with example 2:

  1. ‘jquery’ is a common tag to every item. How should that be handled?
  2. When i uncheck ‘webdev’, an item that has the class of ‘resources’ also disappears. It’s the only item with the class of ‘resources’, yet it’s checkbox stays checked, misleading me into thinking that because it’s checked, there must be a listing showing that has it.
  3. It would be nice to alphabetize the filters.

The latter one was the easiest to knock out. I can sort the array of unique class names by adding this line to the JavaScript:

arrayUniqueClasses = arrayUniqueClasses.sort();

Also, I chose to exclude any class that is common to all elements from being in the filter column. (My code is probably cruddy for doing this – would love feedback on this.) For sake of demonstration, I created a paragraph above the filters that states which classes those are common to all list items.

I noticed an additional challenges:

  1. When a filter is unchecked, before hiding the list item, I need to check other classes on the list item to be hidden.
  2. When a filter is checked again, in addition to showing the list item, I need to check all the other filters for every other class associated with a newly shown list item.
  3. For my personal preference, I wanted to add this in: If all other filters are checked, go ahead and recheck the ‘show all’ checkbox.

To see how I tackled these challenges, I recommend viewing source on the examples. The script is commented with what I’m doing and questions to myself throughout the process.

Questions for people with more jQuery/JavaScript experience than me:

  • In order to have only unique values in my array, I’m using a function called unique(a) (credit: Johan Känngård, http://johankanngard.net/). I tried to use jQuery.unique, but it wasn’t working right. If anyone can do the unique part in jQuery for me and point out how it’s done, I’d be much appreciative.
  • I’m using what is probably an unreliable method for determining if a tag is common to every result. I count the number of list items, and I count the number of times any class appears. If a class’s total occurrences equals the total of all list items, I assume that class is common to all list items. Is there a better way to do this?
  • I’m removing items from my arrays using a removeItems(array, item) function that I got from who-knows-where. Is there a way I can do this in jQuery?
  • Any other things that could be done better?

CSS Guy said:
@Alex:
Great question. I certainly didn’t account for what happens when only one result is listed.
I’d add this as the first line after the $(document).ready(function () { part:
if ($(‘.filterThis > li’).length < 2) { return; } This checks to see if the number of results is less than 2 (such as one or none), and if so, just escape out of the loop. This way, no checkboxes would be created and no sentence regarding excluded filters would be created either. Does that help? I am using your script to filter my online portfolio. I have four tags set up (logo, website, marketing, print). Lets say an li has a class of "website logo" (two tags). When I uncheck logo I still want that element to remain (not disappear) because it still has a tag of website (which is still checked). I basically want to set your script so an element only disappears if all the tags are unchecked, not just one. How can I do that? CSS Guy said: @Jesse, I think I know what you mean. Does this example help? Jesse portfolio filters

CSS Guy said:
@Bryan,
Great question. I didn’t accommodate for tags that have spaces in them.
I’m not sure I can help. I viewed source on my final example and even though I wrote the code, it made my eyes roll into the back of my head. I wish I had time to make this more flexible, but without sitting down to do it again from scratch, I’m not sure I can.
Sorry I couldn’t help further. If you figure something out, send it over and post a comment. I’d be more than happy to link to what you’ve got.