SitePoint Sponsor

User Tag List

Results 1 to 9 of 9
  1. #1
    SitePoint Member
    Join Date
    Sep 2005
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Enhancing Robert Nyman's addClassName

    I need help modifying a function.

    Robert Nyman (www.robertnyman.com) wrote a small collection of functions that handle some dom manipulation, and I'm attempting to use them. One of the functions is called addClassName, and looks like this:

    Code:
    function addClassName(oElm, strClassName){
    	var strCurrentClass = oElm.className;
    	if(!new RegExp(strClassName, "i").test(strCurrentClass)){
    		oElm.className = strCurrentClass + ((strCurrentClass.length > 0)? " " : "") + strClassName;
    	}
    }
    He sums it up as follows (emphasis mine):
    A function to add a class name to an element. It automatically takes into consideration if the element already has that class, and doesnít apply it again if thatís the case. It also adds necessary spaces if the element already has other class names. Called like this:
    addClassName(elementReference, "class-name-to-add");
    The problem: I wanted to add a class of "on" to an element that already had a class of "option". It appears that the function notices the "on" at the end of "optiON", falsely assumes that element already has "on" as a class, and chooses not to add the new class.

    Does anyone know how to modify the function to accommodate for these kinds of scenarios? I've tried to write the author directly, but he's on family leave.

    Please note that I'm well aware of javascript libraries out there that can add a class name. My goal with this post is not so much to adopt a new function, but to learn how this current function would change to fix this shortcoming.

    Any help is greatly appreciated.
    Tony

  2. #2
    SitePoint Evangelist
    Join Date
    Jul 2007
    Posts
    345
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You need some way of saying that the class you're searching for is either at the beginning of the class string or preceded by a space and at the end of the class string or followed by a space.
    Try
    Code:
    RegExp("(^| )" + strClassName + "( |$)", "i")

  3. #3
    SitePoint Wizard chris_fuel's Avatar
    Join Date
    May 2006
    Location
    Ventura, CA
    Posts
    2,750
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Safest way would be to split based on a space and iterate through the values to check:

    Code JavaScript:
    function addClassName(oElm, strClassName){
      var splitClassName = oElm.className.split(' ');
      var classFound = false;
      for(var i = 0 ; i < splitClassName.length; i++)
      {
         if(splitClassName[i] == strClassName)
         {
            classFound = true;
            break;
         }
      }
      if(!classFound)
      {
        oElm.className = strCurrentClass + ((oElm.className.length > 0)? " " : "") + strClassName;
      }
    }

    You can actually do the same with regex, but it's 9:23 in the morning and I really don't want to think about all the possible regex conditions :P. Note I haven't tested this so, there might be syntax errors or something stupid :P.

  4. #4
    I meant that to happen silver trophybronze trophy Raffles's Avatar
    Join Date
    Sep 2005
    Location
    Tanzania
    Posts
    4,662
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    I agree with chris. Personally, I prefer splitting it into two functions:
    Code Javascript:
    function hasClass(el, c) {
      if (!el || !el.className.length) return;
      var bits = el.className.split(' '), has = false;
      for (var j = 0; j < bits.length; j++) if (bits[j] === c) has = true;
      return has;
    }
    function addClass(el, c) {
      if (!el || hasClass(el, c) === true) return;
      if (el.className.length) el.className += ' '+c;
      else el.className = c;
    }
    Then you can use hasClass by itself, which I have found is often useful.

  5. #5
    SitePoint Evangelist
    Join Date
    Jul 2007
    Posts
    345
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Is there an advantage in using the longer split method over using regular expressions?

  6. #6
    SitePoint Wizard chris_fuel's Avatar
    Join Date
    May 2006
    Location
    Ventura, CA
    Posts
    2,750
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would say the main reason is readability. Regular expressions tend to be confusing unless you've worked with them a lot. A string split plus a standard array loop is a little easier for people to work with.

    As for performance, for most general use cases (let's say 3 classes and you're adding a class to a single element), I don't see the performance benefit.

    Another reason is that with regular expressions, I have to consider all possible combinations of how that particular string could occur, versus considering that I know correctly done html class definitions will separate all classes with spaces, so by splitting and comparing, I don't have to worry about missing a particular condition.

  7. #7
    SitePoint Member
    Join Date
    Sep 2005
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you r51, Chris, and Raffles. Your replies are very helpful. It's interesting to also read your thoughts on being selective when implementing regular expressions. Tony

  8. #8
    SitePoint Guru
    Join Date
    Apr 2006
    Posts
    802
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Code:
    String.prototype.stripWords= function(what){
        if(what.constructor != RegExp) what= RegExp("\\b"+what+"\\b",'g');
        return this.replace(what,'');
    }
    
    function reClass(who, new_class, old_class){
        try{
            var temp= who.className, A;
            if(new_class){
                temp= temp.stripWords(new_class);
                A= temp.split(/ +/);
                if(old_class !== false){
                    if(old_class) A= temp.stripWords(old_class).split(/( +)/);
                    A.push(new_class);
                }
                if(A.length) who.className= A.join(' ');
                else who.className= '';
            }
        }
        catch(er){
            return false
        }
        return who.className;
    }

  9. #9
    SitePoint Member
    Join Date
    Sep 2005
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Robert Nyman updates it himself

    I got an email from Robert Nyman, and he updated his own functions with the following.

    I should update EJ, but it won't happen right now, due to lack of time. But, to solve your problem, replace the addClassName and removeClassName functions with this, and it should be fine:

    Code:
    function addClassName(elm, className){
        var currentClass = elm.className;
        if(!new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i").test(currentClass)){
            elm.className = currentClass + ((currentClass.length > 0)? " " : "") + className;
        }
        return elm.className;
    }
    Code:
    function removeClassName(elm, className){
        var classToRemove = new RegExp(("(^|\\s)" + className + "(\\s|$)"), "i");
        elm.className = elm.className.replace(classToRemove, "").replace(/^\s+|\s+$/g, "");
        return elm.className;
    }


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •