Reputation: 3611
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
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.
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
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