jQuery removeClass wildcard
Asked Answered
M

18

414

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!

Marron answered 15/4, 2010 at 10:1 Comment(3)
A regex like in the "accepted answer" will break on non-space word boundaries. I've posted an alternative solution below. Also see: #58312Oliphant
@KabirSarin the accepted answer has since been updated to fix this.Filiano
css wildcards surfingsuccess.com/css/…Mesopause
Q
751

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/

Quern answered 3/3, 2011 at 14:35 Comment(6)
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 ) || [];Facies
I like this solution best! How can I check for two or more classes to be removed? i.e. sport-, nav- and color- ?Lydia
How would you do this to match a class ending like *-color?Warnerwarning
@Lydia 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.Malonylurea
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/.Amalgam
How can you pass a variable to the statement? var myVar="color-"; return (className.match (/(^|\s)"+myVar+"S+/g) || []).join(' '); won't work!Bactericide
W
106
$('div').attr('class', function(i, c){
    return c.replace(/(^|\s)color-\S+/g, '');
});
Whoremaster answered 15/4, 2010 at 10:13 Comment(7)
I like this as it reduces the overhead and gets straight to the point. Why use remove class when attr does the job better?Individualize
i think you need to protect against empty classes (c is undefined)? At least when I tried it below in my plugin on this page, $('a').stripClass('post', 1) threw "TypeError: Cannot call method 'replace' of undefined"Hague
@Hague - 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.Whoremaster
@Whoremaster 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: jsfiddle.net/drzaus/m83mvHague
@Hague - You test does return the element, but that doesn't help you in your plugin, because you iterate over the class attributes: jsfiddle.net/m83mv/3 You can get undefined in some cases, which would break my code.Whoremaster
@Whoremaster - 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. jsfiddle.net/drzaus/m83mv/4Hague
Adding a quick test works for me: return c && c.replace(/\bcolor-\S+/g, '');Sastruga
A
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' )
Abdominal answered 30/12, 2011 at 14:4 Comment(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');Sunderance
H
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');
Handspike answered 20/10, 2016 at 15:22 Comment(1)
I like the jquery extension approach. Thank you !Bruges
M
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.

Moen answered 4/9, 2013 at 18:25 Comment(0)
S
6

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>
Sanitary answered 7/11, 2018 at 13:52 Comment(0)
T
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

Tattoo answered 25/8, 2015 at 9:14 Comment(2)
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.Highlander
@ToddHorst thank you, I've updated the answer. In fact the difference is just shorter code vs better performance.Tattoo
M
5

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.

Meadow answered 3/5, 2020 at 15:23 Comment(1)
beautiful and cleanDawes
H
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://mcmap.net/q/86188/-jquery-removeclass-wildcard
    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

Hague answered 27/9, 2013 at 20:31 Comment(5)
For the record, I disagree that this is simpler.Moen
@Moen sorry, i meant simpler in typical usage (i.e. prefixes and suffixes), since you don't have to write a regex each timeHague
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.Moen
@Moen 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? ;)Hague
There's nothing hard about reading documentation, but as @Moen says, his solution is self-explanatory, while yours requires documentation. Self-explanatory solutions are simpler to use by definition.Neron
S
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/

Summitry answered 5/8, 2014 at 9:43 Comment(1)
Added var prefix='color'; and changed... if (classArr[i].substr(0, prefix.length) != prefix)Tread
A
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.

Attis answered 10/10, 2018 at 14:54 Comment(2)
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 :)Caesalpiniaceous
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.Attis
M
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;
});
Mazdaism answered 23/7, 2015 at 19:24 Comment(1)
Can you explain what the underscore does in _.filterHomosexual
H
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;
};
Hudis answered 6/1, 2016 at 12:58 Comment(0)
P
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, ''));
    });
};
Plautus answered 21/2, 2016 at 10:44 Comment(2)
Wouldn't it be more efficient to create the RegExp object once, out of the each callback?Filiano
@EmileBergeron - yes it would be! I will make changesPlautus
C
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, ''));
Craner answered 28/12, 2011 at 16:7 Comment(0)
O
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;
};
Oliphant answered 1/6, 2014 at 23:11 Comment(0)
K
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.

Kucera answered 14/12, 2015 at 16:48 Comment(0)
M
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.

Memberg answered 10/6, 2016 at 12:57 Comment(3)
That's irrelevant here. You could have answered your own question as it is an encouraged behavior on SO.Filiano
@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?Memberg
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.Filiano

© 2022 - 2024 — McMap. All rights reserved.