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');
Sign up to request clarification or add additional context in comments.

5 Comments

Touche, didn't think to go this route. +1 for showing a different interpretation.
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: 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.
Why do you use a regex for .split()?
@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.
3
$('[class*=" "]')

Returns all tags with a space in the class attribute.

2 Comments

This would match <div class=" foo">
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.
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 Comments

But this requires you to know the names of the classes.
@JonH: Updated for a bit better solution. ;-)
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/

Comments

0

It works just like regular CSS

$('.class1.class2') // will select elements with both classes

1 Comment

That requires you to know the classes beforehand, and it only selects specific elements with multiple classes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.