2

Just like in jQuery we can use $(".classname"), is there a similar thing in javascript as well? Or if we don't have such method then how can i implement that.
According to http://www.dustindiaz.com/getelementsbyclass/ i will have to iterate through all tags and then collect all elements which are having the same class as specified.

Is there any better solution?

Rakesh Juyal
  • 35,919
  • 68
  • 173
  • 214

5 Answers5

14

i will have to iterate through all tags and then collect all elements which are having the same class as specified.

Yes. There are a couple of ways to improve the function from the page you linked, though:

  • RegExp-escape class names passed in so that it isn't broken by punctuation in the class name. For example a call to getElementsByClassName('ab.cd') shouldn't match class="abxcd".

  • The HTML5 specification that defines getElementsByClassName allows multiple space-separated class names, all of which have to be present on the element to match. Implement this.

  • optionally, allow a hint of tag name to be passed in to narrow down the number of elements the function has to look through for the common case where only one type of tag is affected. (This will do nothing on a real browser-native getElementsByClassName call, so you shouldn't rely on it to filter out elements you don't want.)

Example implementation:

if (!('getElementsByClassName' in document)) {
    document.getElementsByClassName= function(classnames, taghint) {
        var exps= classnames.split(/\s+/).map(function(name) {
            name= name.replace(/([/\\^$*+?.()|[\]{}])/g, '\\$1');
            return new RegExp('(^|\\s)'+name+'(\\s|$)');
        });
        var els= this.getElementsByTagName(taghint || '*');
        var matches= [];
        for (var i= 0, n= this.length; i<n; i++)
            var el= els[i];
            if (exps.every(function(exp) {
                return exp.test(el.className);
            }))
                matches.push(el);
        }
        return matches;
    };
}

However this does also use a couple of ECMAScript Fifth Edition (or JavaScript 1.6) Array methods which IE doesn't yet have either, so you'd have to add fallback implementations for those, too:

if (!('map' in Array.prototype)) {
    Array.prototype.map= function(mapper, that) {
        var other= new Array(this.length);
        for (var i= 0, n= this.length; i<n; i++)
            if (i in this)
                other[i]= mapper.call(that, this[i], i, this);
        return other;
    };
}

if (!('every' in Array.prototype)) {
    Array.prototype.every= function(tester, that) {
        for (var i= 0, n= this.length; i<n; i++)
            if (i in this && !tester.call(that, this[i], i, this))
                return false;
        return true;
    };
}
bobince
  • 528,062
  • 107
  • 651
  • 834
  • @bobince: why don't you cache `this.length`? The callback may mutate the array, at which point this implementation diverges from the one built into 1.6. – Crescent Fresh Nov 30 '09 at 15:33
  • Good point, it does seem to be a requirement of the 2009-050 Final Draft that this behaviour is reproduced. Updated. – bobince Nov 30 '09 at 17:00
5

Unfortunately not consistently between browsers. If you don't need all of jQuery, but still would like to do selection based on CSS selectors, take a look at Sizzle, the selector library jQuery uses.

AKX
  • 152,115
  • 15
  • 115
  • 172
3

Another good implementation of getElementsByClassName

Mixes an XPath and DOM implementation; using XPath wherever possible.

if (!('getElementsByClassName' in document)) {
    document.getElementsByClassName = function(className, parentElement) {
        if (Prototype.BrowserFeatures.XPath) {
            var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
            return document._getElementsByXPath(q, parentElement);
        } else {
            var children = ($(parentElement) || document.body).getElementsByTagName('*');
            var elements = [],
                child;
            for (var i = 0, length = children.length; i < length; i++) {
                child = children[i];
                if (Element.hasClassName(child, className)) elements.push(Element.extend(child));
            }
            return elements;
        }
    };
}​


P.S: Posting it here because I guess its faster than bobince answer. No offense :)
Got this from John Resig's getElementsByClassName Speed Comparison
Community
  • 1
  • 1
naveen
  • 53,448
  • 46
  • 161
  • 251
2

Some browsers, for example Firefox 3 support getElementsByClassName, others you have to iterate though all the tags, so if you wanted to support all browsers with a single function you should use the iterative method.

The best solution is to use jQuery or any other framework which will use the best available method.

Yacoby
  • 54,544
  • 15
  • 116
  • 120
  • 2
    Actually jQuery 1.3.2 plumps for `querySelectorAll` before `getElementsByClassName`, which at least on my browser (Firefox 3.5) makes it much slower. Still way faster than JS-native element filtering of course, but not the best available. – bobince Nov 30 '09 at 13:03
  • `querySelectorAll` is in fact expected to be slower than `getElementsByClassName`. Somebody should probably file a bug against jQuery. – sdwilsh Nov 30 '09 at 20:00
  • Also, it'd be interesting to know what benchmark you used for that performance test. The smaller the DOM, the slower they will look, but in larger DOM trees, the difference will be negligible. – sdwilsh Nov 30 '09 at 20:07
2

Yes, you'd have to iterate to support all browsers. If you do, tho, make sure you take advantage of the browser's built in functionality where it exists:

if(document.getElementsByClassName) {
   return document.getElementsByClassName(className);
} else {
   // iterate
}

Other than that, I'm with Jochen; use a framework

David Hedlund
  • 128,221
  • 31
  • 203
  • 222
  • `if (document.getElementsByClassName)`, or `if (typeof document.getElementsByClassName)==='function')` — the above won't quite work as it'll test the string `'undefined'` for truthiness successfully. – bobince Feb 16 '10 at 20:49
  • wow, yes, i must've interrupted my own train of thought there. hey, how come you check for identity in the first version, tho, rather than equality? ..given that `typeof` always yields a string? or do you just consider that a best practice? – David Hedlund Feb 16 '10 at 21:27