Creating custom selectors with jQuery

I recently re-discovered a powerful, but mostly hidden feature of jQuery. If you’ve used jQuery for any length of time you’ve probably used filter selectors like :input, :visible or :hidden. What you may not know is, it is really simple to add your own filter selectors. The secret lies in $.expr.filters which is an object of filters. By adding into this object you can define new filters, or redefine existing ones. My specific case was I needed to filter elements to only those that could get tab focus. Defining a filter is simple and looks something like:

Show Plain Text
  1. (function ($) {
  2.  
  3.     // Visible not disabled elements can have focus.
  4.     // Pretends that image maps don't exist.
  5.     function canFocus(element, hasTabIndex) {
  6.         var nodeName = element.nodeName.toLowerCase();
  7.         if (/input|select|textarea|button|object/.test(nodeName) && !element.disabled) {
  8.             // Similar to $(element).is(':visible');
  9.             return $.expr.filters.visible(element);
  10.         }
  11.         if (nodeName === 'a' && element.href || tabIndex) {
  12.             // Similar to $(element).is(':visible');
  13.             return $.expr.filters.visible(element);
  14.         }
  15.         return false;
  16.     }
  17.  
  18.     // Add the :tabable filter.
  19.     $.expr.filters.tabable = function (element) {
  20.         var tabIndex = $.attr(element, 'tabindex'),
  21.             tabIndexNan = isNaN(tabIndex);
  22.         if (tabIndexNan || tabIndex >= 0) {
  23.             return canFocus(element, tabIndexNan);
  24.         }
  25.     };
  26. }(jQuery));

With only a few lines of code, you can create really expressive filters that let you write expressive selectors. As you can see above, filter functions get an element as their only argument. Filter functions should return a boolean, indicating whether the element passes or fails the filter’s criteria. You could use our new tabable filter by doing:

Show Plain Text
  1. // Get all the tab focusable items inside #modal
  2. var tabbers = $('#modal').find(':tabable');

Hopefully you can find use for this powerful and somewhat hidden jQuery feature in future projects. They are a great way to add expressive ways to select/filter elements based on data stored in the DOM.

Comments

Nice! I’m often pleasantly surprised about jQuery’s extensibility. Another fun hidden one is cssHooks – getters and setters for css properties (and I imagine custom ones).

Jeremy on 12/4/12

Have your say: