413

Is there any easy way to remove all classes matching, for example,

color-*

so if I have an element:

<div id="hello" class="color-red color-brown foo bar"></div>

after removing, it would be

<div id="hello" class="foo bar"></div>

Thanks!

Govind Samrow
  • 9,981
  • 13
  • 53
  • 90
atp
  • 30,132
  • 47
  • 125
  • 187
  • 1
    A regex like in the "accepted answer" will break on non-space word boundaries. I've posted an alternative solution below. Also see: http://stackoverflow.com/questions/57812/remove-all-classes-that-begin-with-a-certain-string – Kabir Sarin Jun 01 '14 at 23:10
  • @KabirSarin the accepted answer has since been updated to fix this. – Emile Bergeron Jul 28 '16 at 18:03
  • css wildcards http://www.surfingsuccess.com/css/css-wildcard-css-attribute-selector.html#.WHYitbZ95rE – ChatGPT Jan 11 '17 at 12:20

18 Answers18

746

The removeClass function takes a function argument since jQuery 1.4.

$("#hello").removeClass (function (index, className) {
    return (className.match (/(^|\s)color-\S+/g) || []).join(' ');
});

Live example: http://jsfiddle.net/xa9xS/1409/

Áxel Costas Pena
  • 5,886
  • 6
  • 28
  • 59
jimmystormig
  • 10,672
  • 1
  • 29
  • 28
  • 4
    I thought it was worth noting that this will capture a leading whitespace of matched classes that don't start at the beginning. Javascript doesn't support positive lookbehind so you would have to a use capture group workaround. However it is a moot, because the removeClass function will strip whitespace from your class string for you via `classes = ( value || "" ).match( rnotwhite ) || [];` – eephillip Jan 07 '15 at 22:11
  • 1
    I like this solution best! How can I check for two or more classes to be removed? i.e. `sport-`, `nav-` and `color-` ? – lowtechsun Feb 20 '16 at 15:09
  • How would you do this to match a class ending like `*-color`? – Circle B Apr 14 '16 at 21:17
  • 5
    @lowtechsun you can add in your regex like this `(color-|sport-|nav-)`. It will match `color-`, or `sport-`, or `nav-`. So, the answer above would become `/(^|\s)(color-|sport-|nav-)\S+/g`. – Bogie Jun 13 '16 at 06:15
  • 3
    The accepted answer is correct, but idiomatically, I'd use \b which matches on any word boundary rather than the more wordy (^|\s). So, for example, to remove all classes consisting out of the word magic followed by a non-negative integer and only those, I'd match on /\bmagic\d+\b/. – CarlEdman Sep 21 '16 at 12:11
  • How can you pass a variable to the statement? var myVar="color-"; return (className.match (/(^|\s)"+myVar+"S+/g) || []).join(' '); won't work! – Tom Senner Jul 12 '17 at 13:12
106
$('div').attr('class', function(i, c){
    return c.replace(/(^|\s)color-\S+/g, '');
});
Krzysztof Janiszewski
  • 3,763
  • 3
  • 18
  • 38
Kobi
  • 135,331
  • 41
  • 252
  • 292
  • 5
    I like this as it reduces the overhead and gets straight to the point. Why use remove class when attr does the job better? – Angry Dan Sep 16 '11 at 14:36
  • 2
    i think you need to protect against empty classes (`c is undefined`)? At least when I tried it [below in my plugin](http://stackoverflow.com/a/19059585/1037948) on this page, `$('a').stripClass('post', 1)` threw "TypeError: Cannot call method 'replace' of undefined" – drzaus Sep 27 '13 at 20:34
  • @drzaus - Good point. Another way to check that is to filter elements with classes: `.filter('[class]')`, though I'm not sure it is impossible to have a defined `class=undefined`. – Kobi Sep 27 '13 at 21:02
  • 1
    @Kobi yeah i don't think it's possible to filter on an attribute and return a result that doesn't have it -- `$('div[class]')` should only return elements with `class`, whether they have a value or not. testing scenarios: http://jsfiddle.net/drzaus/m83mv/ – drzaus Oct 01 '13 at 20:13
  • @drzaus - You test does return the *element*, but that doesn't help you in your plugin, because you iterate over the `class` attributes: http://jsfiddle.net/m83mv/3/ You can get `undefined` in some cases, which would break my code. – Kobi Oct 02 '13 at 04:58
  • 1
    @Kobi - I think i see what you're getting at; in the edge cases like `.removeProp('class')` or `.className=undefined` the filter `[class]` still returns something, they're just `undefined`. So technically it still has a class (as opposed to `.removeAttr('class')`, which is why it breaks your function but not my variant. http://jsfiddle.net/drzaus/m83mv/4/ – drzaus Oct 08 '13 at 13:53
  • 2
    Adding a quick test works for me: `return c && c.replace(/\bcolor-\S+/g, '');` – ssmith Feb 07 '14 at 00:12
55

I've written a plugin that does this called alterClass – Remove element classes with wildcard matching. Optionally add classes: https://gist.github.com/1517285

$( '#foo' ).alterClass( 'foo-* bar-*', 'foobar' )
Pete B
  • 1,709
  • 18
  • 11
  • 1
    Very nice indeed... helped me reuse some code where I just needed to remove `Twitter Boostrap` classes like this: `$(".search div").children('div').alterClass('span* row-fluid');` – Leniel Maccaferri Jun 18 '13 at 21:42
18

If you want to use it in other places I suggest you an extension. This one is working fine for me.

 $.fn.removeClassStartingWith = function (filter) {
    $(this).removeClass(function (index, className) {
        return (className.match(new RegExp("\\S*" + filter + "\\S*", 'g')) || []).join(' ')
    });
    return this;
};

Usage:

$(".myClass").removeClassStartingWith('color');
Charly Guirao
  • 489
  • 3
  • 8
17

I've generalized this into a Jquery plugin which takes a regex as an argument.

Coffee:

$.fn.removeClassRegex = (regex) ->
  $(@).removeClass (index, classes) ->
    classes.split(/\s+/).filter (c) ->
      regex.test c
    .join ' '

Javascript:

$.fn.removeClassRegex = function(regex) {
  return $(this).removeClass(function(index, classes) {
    return classes.split(/\s+/).filter(function(c) {
      return regex.test(c);
    }).join(' ');
  });
};

So, for this case, usage would be (both Coffee and Javascript):

$('#hello').removeClassRegex(/^color-/)

Note that I'm using the Array.filter function which doesn't exist in IE<9. You could use Underscore's filter function instead or Google for a polyfill like this WTFPL one.

tremby
  • 9,541
  • 4
  • 55
  • 74
5

For a jQuery plugin try this

$.fn.removeClassLike = function(name) {
    return this.removeClass(function(index, css) {
        return (css.match(new RegExp('\\b(' + name + '\\S*)\\b', 'g')) || []).join(' ');
    });
};

or this

$.fn.removeClassLike = function(name) {
    var classes = this.attr('class');
    if (classes) {
        classes = classes.replace(new RegExp('\\b' + name + '\\S*\\s?', 'g'), '').trim();
        classes ? this.attr('class', classes) : this.removeAttr('class');
    }
    return this;
};

Edit: The second approach should be a bit faster because that runs just one regex replace on the whole class string. The first (shorter) uses jQuery's own removeClass method which iterates trough all the existing classnames and tests them for the given regex one by one, so under the hood it does more steps for the same job. However in real life usage the difference is negligible.

Speed comparison benchmark

szegheo
  • 4,175
  • 4
  • 31
  • 35
  • Why two options? Which is preferred? What are the pros/cons of either. PS, I like your answer the best, but it could use some clarification. – Todd Horst Oct 29 '20 at 14:47
  • @ToddHorst thank you, I've updated the answer. In fact the difference is just shorter code vs better performance. – szegheo Nov 03 '20 at 07:47
5

A generic function that remove any class starting with begin:

function removeClassStartingWith(node, begin) {
    node.removeClass (function (index, className) {
        return (className.match ( new RegExp("\\b"+begin+"\\S+", "g") ) || []).join(' ');
    });
}

http://jsfiddle.net/xa9xS/2900/

var begin = 'color-';

function removeClassStartingWith(node, begin) {
    node.removeClass (function (index, className) {
        return (className.match ( new RegExp("\\b"+begin+"\\S+", "g") ) || []).join(' ');
    });
}

removeClassStartingWith($('#hello'), 'color-');

console.log($("#hello")[0].className);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="hello" class="color-red color-brown foo bar"></div>
Fifi
  • 3,360
  • 2
  • 27
  • 53
4

Similar to @tremby's answer, here is @Kobi's answer as a plugin that will match either prefixes or suffixes.

  • ex) strips btn-mini and btn-danger but not btn when stripClass("btn-").
  • ex) strips horsebtn and cowbtn but not btn-mini or btn when stripClass('btn', 1)

Code:

$.fn.stripClass = function (partialMatch, endOrBegin) {
    /// <summary>
    /// The way removeClass should have been implemented -- accepts a partialMatch (like "btn-") to search on and remove
    /// </summary>
    /// <param name="partialMatch">the class partial to match against, like "btn-" to match "btn-danger btn-active" but not "btn"</param>
    /// <param name="endOrBegin">omit for beginning match; provide a 'truthy' value to only find classes ending with match</param>
    /// <returns type=""></returns>
    var x = new RegExp((!endOrBegin ? "\\b" : "\\S+") + partialMatch + "\\S*", 'g');

    // https://stackoverflow.com/a/2644364/1037948
    this.attr('class', function (i, c) {
        if (!c) return; // protect against no class
        return c.replace(x, '');
    });
    return this;
};

https://gist.github.com/zaus/6734731

Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201
  • 1
    For the record, I disagree that this is simpler. – tremby Jul 30 '14 at 01:13
  • @tremby sorry, i meant simpler in _typical_ usage (i.e. prefixes and suffixes), since you don't have to write a regex each time – drzaus Jul 30 '14 at 15:46
  • 3
    I still disagree. With your method you have to read the documentation and remember what exactly the value to `endOrBegin` needs to be. It's not self-explanatory. What's hard about writing a regex? `/^match-at-start/` and `/match-at-end$/` are known to every JS developer. But each to his own. – tremby Jul 30 '14 at 22:11
  • 1
    @tremby other than regexes begging to be abused, a non-regex option more closely matches the existing signature of `addClass`, `removeClass`, and `toggleClass`, which is known to every jQuery developer. What's hard about reading documentation? ;) – drzaus Oct 06 '15 at 20:55
  • 2
    There's nothing hard about reading documentation, but as @tremby says, his solution is self-explanatory, while yours requires documentation. Self-explanatory solutions are simpler to use by definition. – hodgef Jan 08 '16 at 22:25
4

we can get all the classes by .attr("class"), and to Array, And loop & filter:

var classArr = $("#sample").attr("class").split(" ")
$("#sample").attr("class", "")
for(var i = 0; i < classArr.length; i ++) {
    // some condition/filter
    if(classArr[i].substr(0, 5) != "color") {
        $("#sample").addClass(classArr[i]);
    }
}

demo: http://jsfiddle.net/L2A27/1/

meni181818
  • 1,103
  • 1
  • 7
  • 11
  • Added `var prefix='color';` and changed... `if (classArr[i].substr(0, prefix.length) != prefix)` – Rich C Jan 15 '18 at 22:19
4

You could also do this with vanilla JavaScript using Element.classList. No need for using a regular expression either:

function removeColorClasses(element) { for (let className of Array.from(element.classList)) if (className.startsWith("color-")) element.classList.remove(className); }

Note: Notice that we create an Array copy of the classList before starting, that's important since classList is a live DomTokenList which will update as classes are removed.

kzar
  • 2,981
  • 2
  • 17
  • 13
  • I like this methode, but is there a way to make this function more global? For example; To give a parameter to to function with the class name you want to remove? I thing that would be even better :) – Loosie94 Jun 17 '20 at 11:49
  • Well sure, you could just add a second parameter to the function for the class name prefix, then use that instead of "color-" in the comparison in the loop. – kzar Jun 18 '20 at 13:21
4

An alternative way of approaching this problem is to use data attributes, which are by nature unique.

You'd set the colour of an element like: $el.attr('data-color', 'red');

And you'd style it in css like: [data-color="red"]{ color: tomato; }

This negates the need for using classes, which has the side-effect of needing to remove old classes.

rorymorris89
  • 1,144
  • 7
  • 14
2

I had the same issue and came up with the following that uses underscore's _.filter method. Once I discovered that removeClass takes a function and provides you with a list of classnames, it was easy to turn that into an array and filter out the classname to return back to the removeClass method.

// Wildcard removeClass on 'color-*'
$('[class^="color-"]').removeClass (function (index, classes) {
  var
    classesArray = classes.split(' '),
    removeClass = _.filter(classesArray, function(className){ return className.indexOf('color-') === 0; }).toString();

  return removeClass;
});
Lucius
  • 2,794
  • 4
  • 20
  • 42
nynyny
  • 21
  • 1
2

Based on ARS81's answer (that only matches class names beginning with), here's a more flexible version. Also a hasClass() regex version.

Usage: $('.selector').removeClassRegex('\\S*-foo[0-9]+')

$.fn.removeClassRegex = function(name) {
  return this.removeClass(function(index, css) {
    return (css.match(new RegExp('\\b(' + name + ')\\b', 'g')) || []).join(' ');
  });
};

$.fn.hasClassRegex = function(name) {
  return this.attr('class').match(new RegExp('\\b(' + name + ')\\b', 'g')) !== null;
};
Community
  • 1
  • 1
Pascal Polleunus
  • 2,411
  • 2
  • 28
  • 30
2

This will effectively remove all class names which begins with prefix from a node's class attribute. Other answers do not support SVG elements (as of writing this), but this solution does:

$.fn.removeClassPrefix = function(prefix){
    var c, regex = new RegExp("(^|\\s)" + prefix + "\\S+", 'g');
    return this.each(function(){
        c = this.getAttribute('class');
        this.setAttribute('class', c.replace(regex, ''));
    });
};
vsync
  • 118,978
  • 58
  • 307
  • 400
1

You could also use the className property of the element's DOM object:

var $hello = $('#hello');
$('#hello').attr('class', $hello.get(0).className.replace(/\bcolor-\S+/g, ''));
Alexander Wallin
  • 1,394
  • 10
  • 22
0

A regex splitting on word boundary \b isn't the best solution for this:

var prefix = "prefix";
var classes = el.className.split(" ").filter(function(c) {
    return c.lastIndexOf(prefix, 0) !== 0;
});
el.className = classes.join(" ");

or as a jQuery mixin:

$.fn.removeClassPrefix = function(prefix) {
    this.each(function(i, el) {
        var classes = el.className.split(" ").filter(function(c) {
            return c.lastIndexOf(prefix, 0) !== 0;
        });
        el.className = classes.join(" ");
    });
    return this;
};
Kabir Sarin
  • 18,092
  • 10
  • 50
  • 41
0

if you have more than one element having a class name 'example', to remove classes of 'color-'in all of them you can do this:[using jquery]

var objs = $('html').find('.example');
for(index=0 ; index < obj1s.length ; index++){
    objs[index].className = objs[index].className.replace(/col-[a-z1-9\-]*/,'');
}

if you don't put [a-z1-9-]* in your regex it won't remove the classes which have a number or some '-' in their names.

0

If you just need to remove the last set color, the following might suit you.

In my situation, I needed to add a color class to the body tag on a click event and remove the last color that was set. In that case, you store the current color, and then look up the data tag to remove the last set color.

Code:

var colorID = 'Whatever your new color is';

var bodyTag = $('body');
var prevColor = bodyTag.data('currentColor'); // get current color
bodyTag.removeClass(prevColor);
bodyTag.addClass(colorID);
bodyTag.data('currentColor',colorID); // set the new color as current

Might not be exactly what you need, but for me it was and this was the first SO question I looked at, so thought I would share my solution in case it helps anyone.

redfox05
  • 3,354
  • 1
  • 34
  • 39
  • That's irrelevant here. You could have answered your own question as it is an encouraged behavior on SO. – Emile Bergeron Jul 28 '16 at 18:01
  • @Emile: That's true. I might do that as it is not directly answering the original question. Should I leave this answer up or remove it? – redfox05 Jul 28 '16 at 18:16
  • At that point, it's at your own risk, it wasn't down-voted nor upvoted yet. So you could move it to your own question without risk. Also, try to avoid duplicates by checking if there's already a question about this. – Emile Bergeron Jul 28 '16 at 18:18