Thursday, May 13, 2010

jQuery Selectors - Adding a :contains exact match


This post has been updated to make these selectors work with jQuery 1.8


I have included a demo to show you the utility of two selectors that you can easily add to your jQuery library.

The standard ":contains()" selector matches the contents of the element in a case sensitive manner. But it is limited and there have been many times that I've needed to exactly match the contents of an element, so I threw together these two useful selectors.

In this demo, you can see the difference when you see what "td:contains(2)" matches - doing so will match all table cells that contain a "2" including dates that begin and end with "2" and the event titles that contains a "2".

":containsExact()" will grab the innerHTML from the element (the date span in this case) and exactly match it. I made this selector case-insensitve so you won't have to worry about the text case.

":containsExactCase()" will grab the innerHTML from the element (the event span in this case) and exactly match the contents in a case-sensitive manner. Try both "Vacation" and "vacation" in the demo.

":containsRegex()" will grab the innerHTML from the element (also the event span in this case) and use regex to match the contents in either a case-sensitive or insensitive manner (depending on the regex "i" flag). NOTE: because of the way jQuery 1.8 handles the text inside this selector, if parenthesis are used, the text must be wrapped in quotes, e.g. /(red|blue|yellow)/gi will now cause an error, so wrap it in quotes "/(red|blue|yellow)/gi" see the example below:


To include these selectors, just add the following code outside of any $(document).ready(function(){...})
$.extend( $.expr[":"], {
 containsExact: $.expr.createPseudo ?
  $.expr.createPseudo(function(text) {
   return function(elem) {
    return $.trim(elem.innerHTML.toLowerCase()) === text.toLowerCase();
   };
  }) :
  // support: jQuery <1.8
  function(elem, i, match) {
   return $.trim(elem.innerHTML.toLowerCase()) === match[3].toLowerCase();
  },

 containsExactCase: $.expr.createPseudo ?
  $.expr.createPseudo(function(text) {
   return function(elem) {
    return $.trim(elem.innerHTML) === text;
   };
  }) :
  // support: jQuery <1.8
  function(elem, i, match) {
   return $.trim(elem.innerHTML) === match[3];
  },

 containsRegex: $.expr.createPseudo ?
  $.expr.createPseudo(function(text) {
   var reg = /^\/((?:\\\/|[^\/]) )\/([mig]{0,3})$/.exec(text);
   return function(elem) {
    return RegExp(reg[1], reg[2]).test($.trim(elem.innerHTML));
   };
  }) :
  // support: jQuery <1.8
  function(elem, i, match) {
   var reg = /^\/((?:\\\/|[^\/]) )\/([mig]{0,3})$/.exec(match[3]);
   return RegExp(reg[1], reg[2]).test($.trim(elem.innerHTML));
  }

});

10 comments:

  1. Updated the script by changing the "$(a).text()" into "a.innerHTML" to prevent triggering on nested text.

    ReplyDelete
  2. awesome. I wrote a demo using this that will keep growing into an interesting plugin.

    http://jsfiddle.net/kneebreaker/jYHNL/

    This is great!

    ReplyDelete
  3. Thank you so much!, this is exactly what I was looking for, put it in my code, tested, it worked like a charm.

    ReplyDelete
  4. This pretty neat. It works but is it possible to do something like $('.red:containsExact("Motion")) I've tried that and seems like it doesn't allow to use a class or id infront of it.

    ReplyDelete
    Replies
    1. It should work. Look at the demo above, it is finding the table cells using a class name + the custom contains selector.

      Delete
    2. This comment has been removed by the author.

      Delete
  5. I've updated the code in this post to make these selectors work with the changes in jQuery 1.8. Please note that the :containsRegex() selector will need quotes wrapped around the regex to prevent errors.

    ReplyDelete