John Boe
John Boe

Reputation: 3611

How to get the name of the class in JQuery removeClass(function)?

I am trying to find a way how to do it when I have a string of classes like that: last_class;=next_class;^=mce;*=tiny;~=wysiwyg. Where every class is separated with semicolon. It can contain a selector like =,^=,*= or ~=. My first idea was to use selectors to locate the elements which contains these classes because I expect that it is not possible to use these selectors directly in $.removeClass(). But the problem which I need to solve is how to refer to the result found by the selector (I mean the operator like *=,~=,^=). When I found the tag, I need to get the name of the class which I need to remove. How to do it? I know I could use the .removeClass( function ) syntax, but I have no idea what to write inside the function.

What I tried till now:

var selectors = "last_class;=next_class;^=mce;*=tiny;~wysiwyg"
selectors.split(';');
for ( var i = 0; i<selectors.length; i++ )
  {
      if ( selectors[i][1] == "=") // ..mce.., ..tiny.. , ..wysiwyg..
        {
        switch ( selectors[i][0])
          {
          case "^": // starts with
            var selector = 'class^="' + selectors[i].substring(2, 0) + '", '
          break;
          case "*": // contains
            var selector = 'class*="' + selectors[i].substring(2, 0) + '", '
          break;
          case "~": // contains word
            var selector = 'class~="' + selectors[i].substring(2, 0) + '", '
          break;
          }

        }
      else // last_class or next_class
        {
        var selector;
        if ( selectors[i][0] == "=") // next_class
          selector = selectors[i].substring(1, 0);
        selector = '."' + selectors[i].substring(1, 0) + '", '
        $.removeClass(selector);
        }
 }

Final goal is to remove all the classes which are in the list.

Edit: Here is my second try. Not completed, but at this moment I cannot find out why when I return class_name the class is not removed from the element. I debugged it and found the class name is correct.

var selectors = "^=col;~=cl;*=-art-"["remove_classes"].split(';');
for ( var i = 0; i<selectors.length; i++ )
  {
  if ( selectors[i][1] == "=")
    {
    var search = selectors[i].substring(2, selectors[i].length );
    switch ( selectors[i][0])
      {
      case "^": // starts with
        var selector = '[class^="' + search + '"]'
        var regex = new RegExp( "^" + search + ".*", 'i');
        $(selector).removeClass(
          function(){
          for ( var word in this.className.split(" ") )
            {
            var class_name = this.className.match(regex);
            if (class_name)
              return class_name;
            }
          }
        )
      break;
      case "*": // contains
        var selector = '[class*="' + search + '"]'
      break;
      case "~": // contains word
        var selector = '[class~="' + search + '"]'
      break;
      }

    }
  else
    {
    var selector;
    if ( selectors[i][0] == "=")
      selector = selectors[i].substring(1, 0);
    selector = '."' + selectors[i].substring(1, 0) + '", '
    $.removeClass(selector);
    }
  }

Upvotes: 2

Views: 109

Answers (2)

trincot
trincot

Reputation: 350345

This is not that straightforward. When you have ^=mce you'll want to both of the following elements:

<div class="mce_edit other_class"></div>
<div class="other_class mce_edit"></div>

If you translate that selector to [class^=mce] and do $('[class^=mce]'), then you'll only match the first element. In fact, the selector to get both of them should be (space is intended in second part):

$('[class^=mce],[class*= mce]')

For the same reasons, the ~= comparator is not that special, and corresponds to the .classname selector.

Here is a solution to both find the elements and then get the class name(s) from the found elements that need to be removed:

var selectors = 'last_class;=next_class;^=mce;*=tiny;~=wysiwyg';
selectors = selectors.replace(/;/g, '\n'); // to use convenient `m` modifier

var classExtractor = new RegExp(selectors
    .replace(/^(\~?=)?([\w-].*)/gm, ' $2(?![^ ])')
    .replace(/^\*=(.*)/gm, '[^ ]*$1[^ ]*')
    .replace(/^\^=(.*)/gm, ' $1[^ ]*')
    .replace(/^\$=(.*)/gm, '[^ ]*$1(?![^ ])')
    .replace(/\n/g, '|'), 'g');

var selectors = selectors
    .replace(/^(\~?=)?([\w-].*)/gm, '.$2')
    .replace(/^\*=(.*)/gm, '[class*=$1]')
    .replace(/^\^=(.*)/gm, '[class*= $1],[class^=$1]')
    .replace(/^\$=(.*)/gm, '[class*=$1 ],[class$=$1]')
    .replace(/\n/g, ',');

$(selectors).each(function (i, elem) {
    (' ' + this.className).match(classExtractor).forEach(function (match) {
        $(elem).removeClass(match);
    });
});

I left out the less-popular |= CSS comparator, but if you need that one as well, it should not be too hard to add the necessary code.

Remarks about your code

I did not look into your code in detail, but here are a few hints as to why .removeClass() fails:

  • First of all, it would help you debug, if you would log (with console.log()) the final values you pass to .removeClass(). It would certainly help discover some of the reasons of the failures;

  • .match(regex) returns an array, so that explains why the ^= case fails in your second code version. You'd have to return className[0];. There could be other issues though, I did not test this further;

  • You had built some selectors like 'class*="' + search + '"' which should really be in square brackets, like: '[class*="' + search + '"]'.

Upvotes: 1

Jigarius
Jigarius

Reputation: 416

There seems to be no direct approach. You are in the right direction as far as handling things pattern by pattern. Here is a pseudo-code (avoiding RegEx as far as possible to make things easier):

// Create an array of class names.
var classes = $el.attr('class').split(/[\s]+/);

// Create an array of patterns.
var patterns = patterns.split(';');

// Go pattern by pattern!
for (var i in patterns) {

  var pattern = patterns[i];

  // Iterate over classes array
  for (var j in classes) {

    // If classes[j] matches your pattern, remove it from the classes array.
    var match = false;

    // Check pattern 1
    if (0 === pattern.indexOf('^=')) {
    }
    // Check pattern 2
    else if (0 === pattern.indexOf('*=')) {
    }
    // Check default case (if required)
    else {
      // Check for a match
    }

    // If a match is found, remove the element from the classes array.
    // Note: This is not as easy as delete classes[j] (:
    if (match) {

    }

  }

}

// Convert classes back to a string and assign it to the element.
$el.attr('class', classes.join(' '));

I hope it helps. The tricky part would be to match the patterns. The second tricky part would be to remove the items from the classes array.

Upvotes: 0

Related Questions