184

With:

if(element.hasClass("class"))

I can check for one class, but is there an easy way to check whether "element" has any of many classes?

I am using:

if(element.hasClass("class") || element.hasClass("class") ... )

Which isn't too bad, but I am thinking of something like:

if(element.hasClass("class", "class2")

Which unfortunately doesn't work.

Is there something like that?

CDspace
  • 2,639
  • 18
  • 30
  • 36
Björn
  • 12,587
  • 12
  • 51
  • 70

15 Answers15

320
element.is('.class1, .class2')

works, but it's 35% slower than

element.hasClass('class1') || element.hasClass('class2')

JSperf chart showing .hasClass() is faster than .is()

See also this jsbench.me test.

double-beep
  • 5,031
  • 17
  • 33
  • 41
Simon Arnold
  • 15,849
  • 7
  • 67
  • 85
  • 19
    19% slower, big deal. Programmers are more expensive. – Nowaker Jun 05 '12 at 18:09
  • 26
    @DamianNowak if those programmers aren't willing to write what amounts to a trivial amount of more code they probably aren't very expensive ones. – Akkuma Aug 31 '12 at 15:16
  • The faster one is probably more readable too, even though it's longer. – Danyal Aytekin Sep 27 '12 at 12:14
  • 2
    ... um, but I just ran this in jsperf for Chrome 21.0.1180 and the is() method is now about 20% faster. But the hasClass() seems more readable. – Danyal Aytekin Sep 27 '12 at 12:27
  • @Akkuma I can't imagine situation where hasClass(..) will run more than few times per second. But I can imagine how ugly it will become when checking 10 classes with hasClass-es, compared to: elem.is('.a, .b, .c, .d, .e, .f, .g, .h, .i, .j'). – psycho brm Apr 05 '13 at 16:39
  • 3
    @psychobrm: If you have to check 10 classes at once, what you really need is to streamline your class names. There's no reason for such nonsense. :) – cHao Apr 09 '13 at 12:04
  • Recent testing in 2014: Firefox 24.1.1 esr, 'is' 13% slower - IE10, 'is' 20% slower - Chrome 32.0.1700.76, 'hasClass' 10% slower. If you don't support Chrome officially, I would use 'hasClass' still. – ToastyMallows Jan 24 '14 at 18:47
  • 1
    This should be abstracted away behind jQuery's API - it should split on the string and perform the most performant method. I feel like this should be an addition to the existing method since you can't have spaces in class names - it's unambiguous! – Aram Kocharyan Jan 29 '14 at 04:44
  • 7
    Personally cant see the need for performance optimising. 12000 operations per second seems fast enough to me. I'd say use whichever looks nicer to you. Optimise performance when the need arises. – 3Dom Aug 27 '15 at 23:47
  • 2
    There is a word for this - it's called "Premature Optimization". Sure, - if you're running this over a table of 1000 rows - it might be very well worth the optimization. For most people - one-off `.is('.probabbly, .okay')` – pyronaur Sep 01 '16 at 09:01
  • @Norris you should not process thousands of rows on front end x) At least not in any mentioned way. – jave.web Dec 04 '16 at 20:33
  • 2
    As of April 2017, `.is` is 10% faster on Chrome 58, but 20% slower on Firefox 53, and a huge 60% slower on Edge 15 - all tests on Windows 10 1703 – CalvT Apr 27 '17 at 09:13
242

How about:

element.is('.class1, .class2')
Matchu
  • 83,922
  • 18
  • 153
  • 160
  • 10
    No, because that would look for elements that have both classes. I think Marcel is looking for elements with one or more of a number of classes. – Giles Van Gruisen Feb 06 '10 at 22:19
  • Just now noticed that I have 4 id="hello" elements there. Fixed version in order to make validators happy: http://jsbin.com/uqoku/2/edit – Matchu Feb 06 '10 at 22:38
  • 1
    @Matchu I was having the same issue with `.hasClass()` but your solution seems to have done the trick, thanks – Nasir Jul 25 '11 at 10:03
  • 12
    This did not work for me, but `$('element').is('.class1.class2')` did – iamchriswick Mar 17 '16 at 22:58
  • 7
    @iamchriswick: That's slightly different than what OP was asking for. `.class1.class2` will match elements that have _both_ classes, but they were looking for elements that match _either_ class. – Matchu Mar 18 '16 at 18:51
  • What if I have to write a statement like that: if (!$('body').hasClass('page-template-default-template') || !$('body').hasClass('single-programs')) With is? – Porcellino80 May 22 '19 at 15:16
  • @GilesVanGruisen ... The comma acts as an OR condition. just as a CSS selector would. `".foo.bar"` would require both "foo" and "bar" classes – Brad Kent May 17 '23 at 03:26
44
$.fn.extend({
    hasClasses: function (selectors) {
        var self = this;
        for (var i in selectors) {
            if ($(self).hasClass(selectors[i])) 
                return true;
        }
        return false;
    }
});

$('#element').hasClasses(['class1', 'class2', 'class3']);

This should do it, simple and easy.

Kalel Wade
  • 7,742
  • 3
  • 39
  • 55
  • 2
    You are missing a var in the for (i in selectors), thus creating a global. – gremwell Feb 18 '14 at 02:49
  • 4
    A bit of a minor point, but the parameter **selectors** should really be named **classes** or **classNames**, since that's what you're passing in, not selectors. Selectors would have a dot in front of them, as in $('#element').hasClasses(['.class1', '.class2', '.class3']); – jbyrd Sep 24 '14 at 14:42
14

filter() is another option

Reduce the set of matched elements to those that match the selector or pass the function's test.

$(selector).filter('.class1, .class2'); //Filter elements: class1 OR class2

$(selector).filter('.class1.class2'); // Filter elements: class1 AND class2
John Magnolia
  • 16,769
  • 36
  • 159
  • 270
7

How about this?

if (element.hasClass("class1 class2")
Mohammad
  • 21,175
  • 15
  • 55
  • 84
4

here's an answer that does follow the syntax of

$(element).hasAnyOfClasses("class1","class2","class3")
(function($){
    $.fn.hasAnyOfClasses = function(){
        for(var i= 0, il=arguments.length; i<il; i++){
            if($self.hasClass(arguments[i])) return true;
        }
        return false;
    }
})(jQuery);

it's not the fastest, but its unambiguous and the solution i prefer. bench: http://jsperf.com/hasclasstest/10

Mohammad
  • 21,175
  • 15
  • 55
  • 84
  • `Error: $self is not defined` – bagofmilk Mar 06 '19 at 15:50
  • 1
    @bagofmilk My reply has been edited by others since I wrote it 6 years ago. Since I've not used jQuery in years, I don't know exactly what was intended with the edit but originally I had `var $self = $(this);` just before the for-loop – Henrik Myntti Mar 06 '19 at 19:09
3

What about:

if ($('.class.class2.class3').length) {
    //...
}
u01jmg3
  • 712
  • 1
  • 11
  • 31
2

jQuery

if( ['class', 'class2'].some(c => [...element[0].classList].includes(c)) )

Vanilla JS

if( ['class', 'class2'].some(c => [...element.classList].includes(c)) )
Simon Arnold
  • 15,849
  • 7
  • 67
  • 85
  • Your jQuery isn't jQuery, it's identical to the vanilla JS, except you're targetting element[0] instead of just element..? – Andrew West Oct 21 '22 at 14:38
1

What about this,

$.fn.extend({
     hasClasses: function( selector ) {
        var classNamesRegex = new RegExp("( " + selector.replace(/ +/g,"").replace(/,/g, " | ") + " )"),
            rclass = /[\n\t\r]/g,
            i = 0,
            l = this.length;
        for ( ; i < l; i++ ) {
            if ( this[i].nodeType === 1 && classNamesRegex.test((" " + this[i].className + " ").replace(rclass, " "))) {
                return true;
            }
        }
        return false;
    }
});

Easy to use,

if ( $("selector").hasClasses("class1, class2, class3") ) {
  //Yes It does
}

And It seems to be faster, http://jsperf.com/hasclasstest/7

Okan Kocyigit
  • 13,203
  • 18
  • 70
  • 129
1

This is quite old, but hear me out on this one.

$.fn.extend({
  hasClasses: function (selectors) {
      // Setup
      const _id = $(this).attr('id'); // Preserve existing id
      const uuid = generateUUID();    // Create new id for query
      $(this).attr('id', uuid);       // Apply new id to element
     
      // Query to find if element has any of the classes
      const res = selectors.some(cls => !!$(`${uuid}.${cls}`).length);
      
      // Rollback on id 
      if (!_id) $(this).removeAttr("id"); // No Id to begin with
      else $(this).attr('id', _id);       // Preserve old id
      
      // Done
      return res;
    }
})

Instead of trying to find a match between one of the classes in selectors and one of the element's classes we simply apply a temporary id (uuid) to the element and query to find if there exists some element with that temporary id and any of the classes listed in selectors.

This inspired by Kalel Wade's and Simon Arnold's solution but with a minor improvement to performance (benchmarked on jsbench.me).

Note

JSBENCH doesn't allow saving over a limit of a certain amount of characters or words. I had some trouble with the async fetching of random words, so you can get random words manually and use the bench that way.

EDIT:

I just noticed that for my implementation of this I am relying on the id with async calls. I might cause an issue if I need to query the element by id the same time that hasClasses changes the id.

To circumvent this we can just add a unique uuid attribute (literally just the uuid).

Here is the correction:

$.fn.extend({
  hasClasses: function (selectors) {
    // Setup
    const uuid = generateUUID(); // Create new uuid to query later
    $(this).attr(uuid, "");      // Apply uuid to element for query

    // Query to find if element has any of the classes
    const res = selectors.some(cls => !!$(`[${uuid}].${cls}`).length);

    // Remove the uuid attribute
    $(this).removeAttr(uuid);

    // Done
    return res;
  }
})

We could still use an the elements' id if it has one instead of adding an attribute.
I'm not sure if querying ids is faster or not. I referenced this, but by the looks of it there isn't much of a hit with modern browsers. Could still implement using the id if it exists instead of an attribute.

source144
  • 29
  • 2
  • 6
0

use default js match() function:

if( element.attr('class') !== undefined && element.attr('class').match(/class1|class2|class3|class4|class5/) ) {
  console.log("match");
}

to use variables in regexp, use this:

var reg = new RegExp(variable, 'g');
$(this).match(reg);

by the way, this is the fastest way: http://jsperf.com/hasclass-vs-is-stackoverflow/22

Romualds Cirsis
  • 742
  • 6
  • 7
  • Like this answer best, although I suspect that looping through the classes and using a string indexOf test might be even faster still... – terraling Jan 11 '15 at 12:42
0

You can do this way:

if($(selector).filter('.class1, .class2').length){
    // Or logic
}

if($(selector).filter('.class1.class2').length){
    // And logic
}
Hamidreza
  • 1,825
  • 4
  • 29
  • 40
0

If the element is uniquely identifiable (by id for example), you might check something like:

if($("#id.class, #id.class2").length)

If the element is uniquely identifiable among its siblings (for example, it's the only DIV element among them), you might check something like:

if(element.parent().find(">div.class, >div.class2").length)
Lux
  • 71
  • 4
-1

This worked for me:

$('.class1[class~="class2"]').append('something');
Tina
  • 17
  • 3
    This answer has nothing to do with the question asked. – Jezen Thomas Nov 15 '13 at 14:29
  • This is actually a great answer. The above selector syntax grabs all elements with both class1 and class2. Then you can do what you like with it, in this case append. Haven't checked performance compared to other methods but the syntax is nice and succinct. – Tim Wright Feb 20 '15 at 00:50
  • 2
    @TimWright This is a terrible answer. The normal way to select an element with 2 classes is `$('.class1.class2')` with no need to use an attribute selector for the second class. Also, the question is asking how to select an element by one of any, not all classes. – Uyghur Lives Matter May 20 '15 at 18:02
-1

Works for me:

 if ( $("element").hasClass( "class1") || $("element").hasClass("class2") ) {

 //do something here

 }
Rangel R. Morais
  • 2,048
  • 1
  • 12
  • 11