I want to post a quick bit here about three methods I use in the X library which make use of element classnames. First a method to detect if an element has a given class:

X.hasClass = function(element, $class) {
    var pattern = new RegExp("(^| )" + $class + "( |$)");
    //ternary to choose
    return pattern.test(element.className) ? true : false;
};

Breaking that down, you have the function taking two arguments. One, the element to check for the presence of the class name and two, the class name itself. Next, a RegEx is defined which will attempt to match the class name you passed in. The RexEx itself is made so that a portion of a string can be matched because of the way that classnames are applied to elements on the DOM, i.e. “spam eggs vikings”. Last, the ternary (return pattern.test…) executes depending on what the test() method returns. Remember, test() is a function of the RegEx object which returns a bool depending on a positive(or lack of) match. Test() is going to return a ‘true’ of the class name is found, ‘false’ if not.If you are unfamiliar with ternary operators, let’s explain by looking at what an if/else doing the same thing would look like.

if (pattern.test(element.className)) {
    return true;
}
else {
    return false;
}

That works, but it’s not the lean and mean js machine we are going for here. Now, the one line of the ternary:

return pattern.test(element.className) ? true : false;

And it works like this:
an action -something to evaluate- ? do if true : do if false

So, in practice you could check if an element had a class name by calling:

X.hasClass(myElement, 'myClass');

hasClass is going to return a Bool though, so usually you will use it as part of an expression:

if(X.hasClass(myElement, 'myClass') {
    do stuff if true
}

I rarely use hasClass outright, but the next function does.

To add a class to a single element or a collection of elements we have this:

X.addClass = function(element, $class) {
    var i;
    //is the element array-like?
    if(element.length) {
        for (i = 0; i < element.length; i++) {
            if (!X.hasClass(element[i], $class)) {
                element[i].className += element[i].className === "" ?
                $class : " "+$class;
            }
        }
    }
    else { //not array-like
        if (!X.hasClass(element, $class)) {
            element.className += element.className === "" ?
                $class : " "+$class;
        }
    }
    return element;
};

In Summary, addClass takes 2 arguments, an element and a class name, checks to see if the element passed in is a single element or a collection of elements, then checks the element(s) to see if they have the passed in class name (via hasClass). If not, element is then checked for the presence of any other classes. Why? If there is another class name already assigned to the element(s) you need to insert a space in front of the new class being appended. Finally the class name you passed in is appended (as long as it’s not already there) and the element is returned. Let’s combine the get method with this in a couple of practical examples:

Say you have an HTML page with these four divs contained in a fifth div:

<div id="stripes">
    <div id="div1"></div>
    <div id="div2"></div>
    <div id="div3"></div>
    <div id="div4"></div>
</div>

And maybe some styles to go with them:

#div1, #div2, #div3, #div4 {
    height: 50px;
    margin-top:10px;
    border-bottom: 1px solid #000;
}
.green {
    background-color: #7f9e7b;
}
.slate {
    background-color: #424242;
}
.grey {
    background-color: #888;
}

So, on a goofy little test page you might have this:

Classless divs

Classless divs


Since the get method returns either a single element or a collection of them, and since our new addClass function can handle either scenario you could grab all those divs and set their background to say, green, in one pass:

X.addClass(X.get('div', X.get('#stripes')), 'green');

Giving you this:

Now with some class (green to be exact)

Now with some class (green to be exact)

Breaking that expression down, recall that the get method will allow you to pass in a ‘context’ to restrain searching. Because I only wanted the divs inside of the div with the id of “stripes” I passed it (‘#stripes’) in along with the search for the generic ‘div’ tagname:

var divs = X.get('div', X.get('#stripes'));

You can break this up into 2 expressions if you want:

var stripes = X.get('#stripes');
var divs = X.get('div', stripes);

After getting the elements, I sent them to addClass. If you used the more verbose code above you would have:

var stripes = X.get('#stripes');
var divs = X.get('div', stripes);
X.addclass(divs, 'green');

In these examples the fact that addClass returns the element passed in is of no consequence. JavaScript functions always return a value, even if one is not specified (undefined in that case), so specifying element as the return value could just be ignored, but we are going to use it next to combine with removeClass.

Here is that removeClass function:

X.removeClass = function(element, $class) {
    var pattern = new RegExp("(^| )" + $class + "( |$)"),
    i;
    //is element array-like?
    if(element.length) {
        for (i = 0; i < element.length; i++) {
            element[i].className = element[i].className.replace(pattern, "$1");
            element[i].className = element[i].className.replace(/ $/, "");            
        }
    }
    else { //nope
        element.className = element.className.replace(pattern, "$1");
        element.className = element.className.replace(/ $/, "");
    }
    return element;
};

Before doing something pedestrian like just removing the ‘green’ classnames from the divs inside of ‘stripes’ let’s swing for the fence and remove the classnames, then add new ones all in one go. weeee!:

X.removeClass(X.addClass(X.get('div', X.get('#stripes')), 'slate'), 'green');

Now that test page would look like:

Class changed to 'slate'

Class changed to 'slate'

This works because of the fact that addClass is returning the element list containing all of the divs we want removeClass to operate on. If you look close you’ll notice that all you needed to do was to take the previous expression we used for adding the green classes at once:

X.addClass(X.get('div', X.get('#stripes')), 'green');

and wrapping it with the removeClass call:

X.removeClass([--previous expression--], 'slate');

There’s more to breakdown, but not too much. The removeClass function just uses the same methodology that addClass does for checking if element is an array-like object (has a length?) then looks for the passed in classname with a RegExp which will replace a match with an empty string using the ‘$1′ backreference (the second call to replace makes sure no whitespace is left over). Finally the element is returned, just like addClass. And, yes, you could invert the order to addClass(removeClass()), it doesn’t matter

All of these examples could have, obviously, been used on single elements. X.get, X.addClass and X.removeClass will take care of that for you. So if you wanted to change the second div back to green you would go:

X.removeClass(X.addClass(X.get('#div2'), 'green'), 'slate');

Notice there is no need to specify a context when using an id (it should be unique).