3

I need to select all elements that contain multiple classes. The class names don't matter, I just need to select any elements with two or more.

What would the jQuery selector for that look like?

1

5 Answers 5

8

This should select all elements with more than two classes.

$('*').filter(function() {
  return this.className.trim().split(/\s+/).length > 1;
}).foo('bar');
5
  • 1
    Touche, didn't think to go this route. +1 for showing a different interpretation. Commented Oct 4, 2011 at 18:58
  • Not to be mean, but this isn't actually the correct solution because elements that have a single class but have some spaces in the class attribute may get incorrectly selected as well, as I explained below in my answer. Here's an example of this: jsfiddle.net/udBZy/3 Also, $('*') is unnecessarily inefficient.
    – maxedison
    Commented Oct 4, 2011 at 21:04
  • @maxedison: thanks, I didn't actually run the code. As for the efficiency, you could go even further and define a scope that's a bit more specific than *. I just chose the most broad.
    – Blender
    Commented Oct 4, 2011 at 21:44
  • Why do you use a regex for .split()?
    – maxedison
    Commented Oct 5, 2011 at 12:30
  • @maxedison: If he split by a single space, "foo[space][space]bar" would be split into three components, "foo", "" and "bar". The regex is there to clear any contiguous spaces.
    – BoltClock
    Commented Oct 5, 2011 at 15:13
3
$('[class*=" "]')

Returns all tags with a space in the class attribute.

2
  • 1
    This would match <div class=" foo">
    – BoltClock
    Commented Oct 4, 2011 at 19:01
  • It would, which is common especially if you're generating classes in an ERB/JSP/other compiled html template. To use a regex I think you'd have to bump up to Blender's solution. Commented Oct 4, 2011 at 19:11
2

A different understanding leads me to a better solution (my apologies for jumping to a conclusion):

Demo

(function($){
    $.expr[':'].classes = function(o,i,m,s){
        var c = o.className.match(/\s*(.*)\s*/)[0].split(/\s+/).length;

        // Hard [fixed] limit
        // :classes(N)
        if (/^\d+$/.test(m[3])) {
            var n = parseInt(m[3], 10);
            return o.className.split(/\s+/).length == n;
        }

        // Expression:
        // :classes(>N)    :classes(>=N)
        // :classes(>N)    :classes(<=N)
        else if (/^[<>]=?\d+$/.test(m[3])) {
            var e = m[3].match(/^[><]=?/)[0],
                n = m[3].match(/\d+$/)[0];
            switch (e){
                case '<':
                    return c < n;
                case '<=':
                    return c <= n;
                case '>':
                    return c > n;
                case '>=':
                    return c >= n;
            }
        }

        // Range
        // :classes(4-6)
        else if (/^\d+\-\d+$/.test(m[3])) {
            var ln = parseInt(m[3].match(/^(\d+)/)[0], 10),
                hn = parseInt(m[3].match(/(\d+)$/)[0], 10);
            return ln <= c && c <= hn;
        }

        // all else fails
        return false;
    };
})(jQuery);

Updated Added a bit more flexibility with regards to the argument you can supply. Now you have the following options (replacing N & M with numbers):

  • :classes(N)
    Finds elements with exactly N classes
  • :classes(<=N)
    Finds elements with N or fewer classes
  • :classes(<N)
    Finds elements with fewer than N classes
  • :classes(>=N)
    Finds elements with N or more classes
  • :classes(>N)
    Finds elements with more than N classes
  • :classes(N-M)
    Finds elements whose class count falls between N and M
2
  • But this requires you to know the names of the classes.
    – JonH
    Commented Oct 4, 2011 at 18:57
  • @JonH: Updated for a bit better solution. ;-) Commented Oct 4, 2011 at 19:06
1

The following code will first select all elements with a space in the class attribute. We could just do $('*') as Blender suggests, but that's less efficient because it initially selects ALL elements on the page, rather than just those that are viable candidates (i.e., have a space in the class name).

It also takes into consideration those cases where there is just one class, but the class attribute has a space in it (this is done by using jQuery's $.trim() method on the class attribute prior to splitting it). Blender's doesn't solve this kind of situation.

$(function(){
    var div = $('div[class*=" "]').filter(function(){
        var clsArray = $.trim(this.className.split(' ');
        return clsArray.length > 1;
    });
    div.css('background','yellow');
});

Live example: http://jsfiddle.net/udBZy/3/

0

It works just like regular CSS

$('.class1.class2') // will select elements with both classes
1
  • 1
    That requires you to know the classes beforehand, and it only selects specific elements with multiple classes.
    – BoltClock
    Commented Oct 4, 2011 at 18:56

Not the answer you're looking for? Browse other questions tagged or ask your own question.