How to write an removeClass method

JavaScript polyfill, by , Friday, March 28th, 2014

This article is very much in relation to my previous article How to write an addClass method, as I presume that if you ever need to add a class, you most likely also need to remove one or more classes at some point. In this article I will assume that you have the following method available in some outer scope;

  1. var isObjClass = function (o, ocl) {
  2. return Object.prototype.toString.call(o) === '[object ' + ocl + ']';
  3. };

I use this to test if the passed parameters are of the right object class so that we can handle them accordingly. (if this left you wondering what I mean by object class and what this method exactly does, then read my article How to get the class of an object value) The reason we need this test is, that the removeClass functions I am going to show here takes two parameters;

  • obj which is the element or elements being manipulated, and can either be a single DOM element or a NodeList.
  • classes which is the class or classes being removed, and can either be a string or an array.

The reason I have chosen to write them like this is, that it gives more flexibility. We can run it on a single DOM element and we can run it on a collection of DOM elements, and either way, we can remove one or more classes from that or those elements, specifying them either in a string or as an array, what ever is more convenient at that point. Of course you can just choose to view this as inspiration an then go ahead and write it any way you want. So with that said, lets look at the first version.

removeClass with nested for-loop

This is properly the first approach most people would take facing the challenge of writing this type of function. It is easy to write a few for-loops and make things happen like this;

  1. var removeClass = function (obj, cl) {
  2. "use strict";
  3. var i,j,
  4. o = isObjClass(obj, 'NodeList') ? obj : [obj],
  5. cl = isObjClass(cl, 'Array') ? cl : cl.split(' '),
  6. len = {"oLen":o.length, "clLen":cl.length},
  7. targetIndex;
  8. for (i = 0; i < len.oLen; i++) {
  9. o[i].className = (function (clArr) {
  10. for (j = 0; j < len.clLen; j++) {
  11. if ((targetIndex = clArr.indexOf(cl[j])) >= 0) {
  12. clArr.splice(targetIndex, 1);
  13. }
  14. }
  15. return clArr.join(' ');
  16. }(o[i].className.split(' ')));
  17. }
  18. };

The way this works is, that we at line 4 check if we have an NodeList. If we do we store that in o, if not we shove it into an array and store the array in o. This of course makes the assumption, that whatever is not an NodeList is a single DOM element, which is reasonable. Secondly at line 5, we check if the class or classes passed are in an array. If they are not, we again make an assumption, which is that they then are in the form of a string, which we turn into an array and store in cl. From line 9 we iterate over the elements stored in o, and at line 10 we assign the set the className property equal to the result of running the following anonymous function;

  1. o[i].className = (function (clArr) {
  2. for (j = 0; j < len.clLen; j++) {
  3. if ((targetIndex = clArr.indexOf(cl[j])) >= 0) {
  4. clArr.splice(targetIndex, 1);
  5. }
  6. }
  7. return clArr.join(' ');
  8. }(o[i].className.split(' ')));

At line 17 you can see, that we are passing the current elements class name(s) as an array to the function. Inside the function starting from line 11 we iterate over the classes that shall be removed, checking if they are in the passed array of classes on the current element. If it is present, we remove it at line 13. One thing you might have noticed is, that at line 12 inside the if statement check, we make an assignment to the targetIndex. Some people do not like when you do this, but I find it convenient in situations like this. When the iteration is done, we return turn the array of current element classes back into a string and return it. This is all there is to this version of the removeClass function, and as you can see, it is pretty straight forward and easy to read. With that said, I believe we can do better than this, so lets have a look at another approach.

addClass function using forEach

In this version we are going to make use of the Array.prototype.forEach method, which works in all modern browsers. If you have visitors using older browsers like IE8 you can find a Array.prototype.forEach polyfill. As you will see, using build in methods, often simplify things a bit;

  1. var removeClass = function (obj, cl) {
  2. "use strict";
  3. var o = isObjClass(obj, 'NodeList') ? obj : [obj],
  4. cl = isObjClass(cl, 'Array') ? cl : cl.split(' ');
  5. Array.prototype.slice.call(o).forEach(function (elmVal) {
  6. elmVal.className = (function (clArr, tIndex) {
  7. cl.forEach(function (clVal) {
  8. if ((tIndex = clArr.indexOf(clVal)) >= 0) {
  9. clArr.splice(tIndex, 1);
  10. }
  11. });
  12. return clArr.join(' ');
  13. }(elmVal.className.split(' '), undefined));
  14. });
  15. };

As in the previous version, we start out checking the parameters, which we already covered. The interesting things happen from line 6 where we start out turning o into an array, such that we have the forEach method available. If you read the documentation for the forEach method, or read my article about a polyfill for it, you will find that the callback function gets called with three parameters; the current element value, the element index, and the array that we are iterating over. In our case, we are only interested in the current element value, which in the outer forEach method represents the current element (elmVal) being manipulated. In the inner forEach method at line 8 the element value represents the class we are currently attempting to remove. You might have noticed, that like in the first version of the removeClass function, we call an anonymous function, but in this version we pass two parameters at line 14. The first parameter is, like before, the current element classes turned into an array, but the second parameter is just undefined. The reason for this, is the inside the anonymous function we potentially need to store the index of the class to remove, and instead of creating a variable to hold this value, we simply pass an undefined parameter. I find this approach much more clean than the first one, and just as easy to read, but even this is pretty good stuff, lets have a look at the problem using recursion.

addClass function using forEach and recursion

This version is interesting as it removes one layer of forEach traversing by replacing it with recursive function calls, so lets have a look at the code;

  1. var removeClass = function (obj, cl) {
  2. "use strict";
  3. var o = isObjClass(obj, 'NodeList') ? obj : [obj],
  4. cl = isObjClass(cl, 'Array') ? cl : cl.split(' ');
  5. Array.prototype.slice.call(o).forEach(function (elmVal) {
  6. elmVal.className = (function remClass(oClass, rClass, tIndex) {
  7. tIndex = oClass.indexOf(rClass);
  8. return (rClass && tIndex >= 0) ? remClass((function () {
  9. oClass.splice(tIndex, 1);
  10. return oClass;
  11. }()), cl[cl.indexOf(rClass) + 1], undefined) : oClass.join(' ');
  12. }(elmVal.className.split(' '), cl[0]));
  13. });
  14. };

If you remember from the previous versions, we did the assignment to tIndex inside the if statement evaluation, which I stated was convenient. In this example I have not done this, as I believe that it would obscure readability of the code, and also because it would make line 9 longer than necessary.

All of these version of the removeClass function do the same thing being provided the same input, the only difference is in the way the code is written. Feel free to use any of them as you wish.