www.webdeveloper.com
Results 1 to 15 of 15

Thread: Element.prototype.getElementsByClassName

  1. #1
    Join Date
    Dec 2008
    Posts
    488

    Question Element.prototype.getElementsByClassName

    Hi,

    I'm building my own getElementsByClassName to work with all browsers. This is done. It works great, except one thing. I'm trying to replace the new Firefox native method, so that it will work exactly the same and will be predictable across all browsers. It doesn't seem like this is necessary, but I want my error handling to work properly, and any functionality I add to it later.

    Element.prototype.getElementsByClassName=getElementsByClassName is not working in Firefox. Neither is HTMLElement. This is weird, because I got HTMLDocument to apply the new version of the function to the document, but of course the elements are not inheriting the method.

    Is there any way to replace this method? Or is there another object besides "Element" that I need to be targeting?

    I also tried delete Element.getElementsByClassName and Element.getElementsByClassName=null and then redefining it, with no success. I've also tried several other objects like HTMLElement, HTMLhtmlElement, HTMLBodyElement... Node, etc.

    Any ideas?

    Code:
    function getElementsByClassName()
    {
        //for debugging this function:
        //alert('my function');
            if(arguments.length===1 && typeof arguments[0]=='string')
            {
                    $$nArr=new Array();
                    $$cArg=new RegExp('\\b'+arguments[0]+'\\b');
                    $$DOM=this.getElementsByTagName('*');
                    for ($$i=0;$$i<$$DOM.length;$$i++)
                    {
                            $$tmp=$$DOM[$$i].className;
                            if ($$cArg.test($$tmp)) $$nArr.push($$DOM[$$i]);
                    }
                    return $$nArr;
            }
            //throw some errors if not used correctly (for debugging)
            else if(arguments.length>1) throw new Error("Too many arguments specified for \"getElementsByClassName\", use only 1.");
            else throw new Error("Argument [Class Name (string)] expected for [Element].getElementsByClassName().");
    }
    try
    {
        Element.prototype.getElementsByClassName=getElementsByClassName; //Doesn't do anything??? weird
        Object.prototype.getElementsByClassName=getElementsByClassName; //IE
        HTMLDocument.prototype.getElementsByClassName=getElementsByClassName; //works on all "document" objects on all browsers
        HTMLElement.prototype.getElementsByClassName=getElementsByClassName //another attempt. doesn't do anything.
    }
    catch(e)
    {
    }

  2. #2
    Join Date
    Dec 2008
    Posts
    488
    Update:

    I changed the name of the function to see if it was an issue with the object, or the pre-existing function, and this allowed it to work. So it's not an issue with the object name. It appears it's to do with the function already existing in FF. So how do I replace it?

  3. #3
    Join Date
    Aug 2007
    Posts
    3,767
    It is a terrible idea to change inbuilt functions. I would strongly advise that you don't replace anything at all. Also, you have a mistake in your function I think. As far as I remember hyphenated classes names are allowed, and \b matches hyphens. So you have to use the rather inelegant.
    Code:
    $$cArg=new RegExp('($|\\s)'+arguments[0]+'(^|\\s)');
    Prototyping the Object tends to be problematic too, as far as I know (I've never got embroiled with it much so I'm not sure). The solution I would take if I were you would be to use an interface with your library like $ or whatever you want.

  4. #4
    Join Date
    Dec 2008
    Posts
    488
    Ah, these are good things to consider. Thanks for pointing that out about hyphens. I hadn't thought of that. But I checked it out and it works fine. It doesn't match hyphens.

    About prototyping, I could easily use the Sizzle ($) library for selectors, but I'm trying to write a standalone function for a client that is afraid of jQuery for some reason or another, and there's no changing that.

    Since getElementsByClassName isn't a real method and only exists in recent FF versions, I figured it wouldn't be a problem to overwrite it. My code works great for browsers so far, and even the document object accepts the new prototype. It's just the Element object that won't take it. I even changed the names to create a new method on Element, and this worked perfectly. But I'm trying to stick to "getElementsByClassName".

  5. #5
    Join Date
    Dec 2008
    Posts
    488
    Ok, I think you're right. I'm not going to be able to use it the way I hoped. I did find what appears to be a fix, but not the one I was hoping for. I can use an HTC file to make this work, as per an article I found here but I have yet to implement it.

    For now, I've simply added an extra argument to handle the container:

    Code:
    function getElementsByClassName()
    {
            if(
                (arguments.length==1||(arguments.length==2&&typeof arguments[1]=='object'))
                 &&
                (typeof arguments[0]=='string')
               )
            {
                    $$nArr=[];
                    $$cArg=new RegExp('\\b'+arguments[0]+'\\b');
                    $$elem=arguments.length==1?document:arguments[1];
                    $$DOM=$$elem.getElementsByTagName('*');
                    for ($$i=0;$$i<$$DOM.length;$$i++)
                    {
                            $$tmp=$$DOM[$$i].className;
                            if ($$cArg.test($$tmp)) $$nArr.push($$DOM[$$i]);
                    }
                    return $$nArr;
            }
            //throw some errors if not used correctly (for debugging)
            else if(arguments.length==2&&typeof arguments[1]!='object') throw new Error("Specified container is null or not an object in document.getElementsByClassName(\"[className|string],[OBJECT (optional)]\").");
            else if(arguments.length>2) throw new Error("Too many arguments specified for \"getElementsByClassName\", use 2 or less.");
            else throw new Error("Argument [Class Name (string)] expected for [Element].getElementsByClassName().");
    }
    try
    {
         HTMLDocument.prototype.getElementsByClassName=getElementsByClassName; //works on all "document" objects on all browsers
    }
    catch(e)
    {
        try
        {
            document.getElementsByClassName=getElementsByClassName; //For IE7 and before
        }
        catch(err)
        {
            throw new Error("Browser does not support the method getElementsByClassName.");
        }
    }

  6. #6
    Join Date
    Aug 2007
    Posts
    3,767
    Quote Originally Posted by jamesbcox1980 View Post
    Ah, these are good things to consider. Thanks for pointing that out about hyphens. I hadn't thought of that. But I checked it out and it works fine. It doesn't match hyphens.
    It does. It will match "class-name" if you run /\bname\b/.

  7. #7
    Join Date
    Dec 2008
    Posts
    488
    ..
    Last edited by jamesbcox1980; 10-27-2009 at 11:32 AM.

  8. #8
    Join Date
    Dec 2008
    Posts
    488
    Ah, I see, it thinks "-" is the word boundary... I was looking at it differently I guess. Ok, so thanks for pointing that out. Your regexp fixes that. Now, perhaps you could help me understand it. I thought that "$" matched end of input.

  9. #9
    Join Date
    Dec 2003
    Location
    Bucharest, ROMANIA
    Posts
    15,428
    I would put my money on this:
    Code:
    function setClassMethod(){
    if (!document.getElementsByClassName) {
    	document.getElementsByClassName = function (cn) { 
    		var rx = new RegExp("(?:^|\\s)" + cn+ "(?:$|\\s)");
    		var allT = document.getElementsByTagName("*"), allCN = [], ac="", i = 0, a;
    			while (a = allT[i=i+1]) {
    			  ac=a.className;
    			  if ( ac && ac.indexOf(cn) !==-1) {
    				if(ac===cn){ allCN[allCN.length] = a; continue;   }
    				rx.test(ac) ? (allCN[allCN.length] = a) : 0;
    			  }
    			}
    		return allCN;
    	}
    }
    }
    onload=setClassMethod;

  10. #10
    Join Date
    Aug 2007
    Posts
    3,767
    Quote Originally Posted by jamesbcox1980 View Post
    I thought that "$" matched end of input.
    So it does. Yes, it should indeed be /(^|\s)classname($|\s)/, or using lookahead as Kor does /(?:^|\s)classname(?:$|\s)/ has the advantage that the spaces aren't matched as well, important if you are replacing the class name.

  11. #11
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    or using lookahead as Kor does /(?:^|\s)classname(?:$|\s)/
    Not a look-ahead, just non-capturing parentheses. It works just like regular parentheses, but it doesn't remember the match.

    https://developer.mozilla.org/en/Cor...Objects/RegExp

    Search that page for (?:x)
    for(split(//,'))*))91:+9.*4:1A1+9,1))2*:..)))2*:31.-1)4131)1))2*:3)"'))
    {for(ord){$i+=$_&7;grep(vec($s,$i++,1)=1,1..($_>>3)-4);}}print"$s\n";

  12. #12
    Join Date
    Dec 2008
    Posts
    488
    or using lookahead as Kor does /(?:^|\s)classname(?:$|\s)/
    Yeah, i think it's just a pattern delimiter without a capture.

    @Kor, back to my main problem. Setting a method to the document was only half the battle. I pretty much had that taken care of. My main problem was trying to attach the method to the elements as well, and Element.getElementsByClassName=getElementsByClassName isn't allowed in FF or IE. (It's ok for FF, I guess, since it already has a native function, but what can I do for IE?)

    Apprently there's a security issue in IE and you're not allowed to extend the Element object.

  13. #13
    Join Date
    Jul 2008
    Location
    urbana, il
    Posts
    2,787
    Quote Originally Posted by jamesbcox1980 View Post
    My main problem was trying to attach the method to the elements as well, and Element.getElementsByClassName=getElementsByClassName isn't allowed in FF or IE. (It's ok for FF, I guess, since it already has a native function, but what can I do for IE?)
    you can extend native objects in IE8, but for 6+7, your only option is an Object.prototype method.

    If you follow good practice and filter all for-in loops with hasOwnProperty, this should not be a problem.

  14. #14
    Join Date
    Dec 2008
    Posts
    488
    See, I don't even know why they update the JavaScript engine. You still have to be sure to program functionality for older browsers no matter how much they update it.

    Anyway, my laziness aside, Object.prototype did not help for this. You can see in my original post that I tried it, but it only worked in IE8. When I switched to IE7, it stopped working.

  15. #15
    Join Date
    Jul 2008
    Location
    urbana, il
    Posts
    2,787
    firefox won't let you clobber native methods.
    that said, you should never alter the behavior of a native method anyway.

    you will have to add the getElementsByClassName to the element when you fetch it, like jQuery does.

    I would consider adding getElementsByClassName to Array, and collecting arrays instead of collections. That way, you can leave the elements intact, but still have your methods readily available...

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Posting Permissions

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



Recent Articles