Document.getElementByClass?

Is there any way to use document.getElementById but for classes? getElementById sure is useful but sometimes you need to get at a class instead and you can’t always change it to an id.

No. But it is easy enough to write your own function. Get all the elements you are interested in checking, look at the class attribute of the elements, e.g. if(elmt.class == “theClass”), and then add the matching elements to an array. Return the array.

http://www.google.com/search?q=getElementByClass

There are many available. Here’s mine: xGetElementsByClassName

For browsers that support xpath, isn’t it a lot faster to use that?

kyberfabriken, yes, xpath is the fastest solution. Firefox 3 will have (available in trunk builds) a native implementation that will be even faster! :slight_smile:

You mean a native implementation of getElementByClass? How’s the API going to look?

Check out the WHATWG specification

Hi kyber,

That’s a great suggestion. I’m going to start looking into that.

Thanks :slight_smile:

This might be interesting for you guys.

kyber and Pepe: Thanks for the ideas and links. I made a test page - there are two test links (“Test 1” and “Test 2”) in the upper-left corner of the page. Here are my results.


Test 1, Original implementation, Elapsed times:
 Op:  16,32,
 FF:  16,31,46,
 IE7: 78,125
 IE6: 90,109,110,130

Test 2, Test implementation, Elapsed times:
 Op:  0,15,16
 FF:  0,15,16,
 IE7: 78,94,109,110,125,
 IE6: 90,110,150,

Browsers:
 On WinXP: Opera 9.10, Firefox 2.0, IE 7.
 On WinNT: IE 6.

Notes:
 - The browsers must be cacheing something because it always
takes longer when a test is ran just after reloading the page.
 - I clicked each test link repeatedly until the elapsed time
became more consistent.
 - A zero elapsed time just means that it was too fast for my
simple timing technique to measure (I think).

Here’s the test driver and the two functions:


function test(cls, num)
{
  if (num == 1) { // Test 1
    startTime = new Date().getTime();
    xGetElementsByClassName(cls, document, '*',
      function(e){
        e.style.border = '1px dotted red';
      }
    );
    var et1 = new Date().getTime() - startTime;
    alert('Original implementation: ' + et1);/////
  }
  else {          // Test 2
    var startTime = new Date().getTime();
    xGetElementsByClassName2(cls, document,
      function(e){
        e.style.border = '1px dotted blue';
      }
    );
    var et2 = new Date().getTime() - startTime;
    alert('Test implementation: ' + et2);/////
  }
}

function xGetElementsByClassName(c,p,t,f) // original implementation
{
  var r = new Array();
  var re = new RegExp("(^|\\\\s)"+c+"(\\\\s|$)");
  var e = p.getElementsByTagName(t);
//  var e = xGetElementsByTagName(t,p); // See xml comments.
  for (var i = 0; i < e.length; ++i) {
    if (re.test(e[i].className)) {
      r[r.length] = e[i];
      if (f) f(e[i]);
    }
  }
  return r;
}

function xGetElementsByClassName2(c,p,f) // test implementation
{
  var e, i, r = new Array();
  if (!p) p = document;
  if (document.evaluate) {
    var x = document.evaluate(".//*[contains(concat(' ', @class, ' '), ' " + c + " ')]", p, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (i = 0; i < x.snapshotLength; i++) {
      r[r.length] = x.snapshotItem(i);
      if (f) f(x.snapshotItem(i));
    }
  }
/*
  else if (p.getElementsByClassName) { // FF3 ?
    r = p.getElementsByClassName(c);
  }
*/
  else {
    var re = new RegExp("(^|\\\\s)"+c+"(\\\\s|$)");
    e = p.getElementsByTagName('*'); // not for IE5
    for (i = 0; i < e.length; ++i) {
      if (re.test(e[i].className)) {
        r[r.length] = e[i];
        if (f) f(e[i]);
      }
    }
  }
  return r;
}

Everyone’s feedback and further browser tests would be very much appreciated :slight_smile: