which getElementByClass implementations are favored?

T

Thomas 'PointedEars' Lahn

Jake said:
When I search this newsgroup for mentions of getElementByClass I see a
bunch of mentions from 2005, such as this one:

http://groups.google.com/group/comp...?lnk=gst&q=getElementByClass#90b3d891eed83e3e

I haven't needed to use such a function in the last year. I recall
there were a number of implementations, by a number of talented
Javascript programmers. Is there any one of these that eventually came
to be seen as the best?

Not to my knowledge (googling for getElementsByClass[Name] would help
already, wouldn't it?). However, IIRC it has been established that there
are at least three ways to do it: a) DOM built-in (but yet to be
standardized) document.getElementsByClassName(...), b) user-defined
iteration over the return value of document.getElementsByTagName("*") or
document.getElementsByTagNameNS("*") or over document.all.tags, and then
RegExp matching, or c) iteration over an XPath result. ISTM a, c, b should
be the order of alternatives in source code for (X)HTML documents. YMMV.

And for RegExp matching it has been established that using /.../ as
expression does not suffice because it matches infixes; that /\b...\b/ does
not suffice since CSS class names may consist of characters that are
considered word boundaries. IIRC, /(^|[ \t\f\u200b])...([ \t\f\u200b]|$)/
was eventually suggested as a better alternative. (In the process, \s
instead of [ \t\f\u200b] was ruled out because the former matches too many
other characters per Specification, and implementations are incompatible there.)


HTH

PointedEars
 
R

RobG

When I search this newsgroup for mentions of getElementByClass I see a
bunch of mentions from 2005, such as this one:

http://groups.google.com/group/comp.lang.javascript/browse_thread/thr...

I haven't needed to use such a function in the last year.


I recall
there were a number of implementations, by a number of talented
Javascript programmers. Is there any one of these that eventually came
to be seen as the best?

Further to what Thomas said, the method usually returns a collection
or array of element references and therefore is typically given a
plural name of getElementsByClassname.

It has been implemented in a couple of browsers and various libraries,
however browser support will likely be obviated by the W3C Selectors
API:

<URL: http://www.w3.org/TR/selectors-api/ >

which offers a much wider variety of selectors than just class.
 
J

Jorge

(...)

I haven't needed to use such a function in the last year. I recall
there were a number of implementations, by a number of talented
Javascript programmers. Is there any one of these that eventually came
to be seen as the best?

I'd use a "walkTheDom" function:

function walkTheDOM(node, func) {
func(node);
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}

and pass it a proper class-matching f() of your liking: e.g. *not*
this one:

function func (e) {
if (e.className.indexOf(class) >= 0) {
results.push(e);
}
}

var class= "className";
var results= [];
var targetNode= document.body;

walkTheDOM(targetNode, func);

(grep: http://www.google.com/search?q="walkthedom")
 
T

Thomas Allen

Too bad they didn't add document.getElementsByClassName to IE8, since
it's already in Firefox, Opera, and Safari.

Thomas
 
R

RobG

Too bad they didn't add document.getElementsByClassName to IE8, since
it's already in Firefox, Opera, and Safari.

Yes, and the Selectors API returns static lists whereas
getElementsByClassName, when implemented as a host method, typically
returns live collections.
 
D

David Mark

Which we can actually take advantage of right now (e.g. in IE8, which
lacks `document.evaluate`) when implementing `getElementsByClassName`.

I'm thinking about something along these lines (completely untested and
based on HTML5 *draft*):

(function(){
   if (document.querySelectorAll) {

I assume this is just for example, but this is not a good way to test
methods of host objects. And as you propose to overwrite the gEBCN
property, you should also check for the non-existence of that
property. I would propose that you not augment the document object at
all.

[snip]
 
D

David Mark

Of course. `isHostMethod` would undoubtedly be safer than boolean type
conversion.


Err. Overwriting `document.getElementsByClassName` was a clear mistake
on my part. That should have been just `getElementsByClassName` of course..

I tried following specs here, but all this stripping and tokenizing
seems like an overkill. I, for one, can't remember giving
`getElementsByClassName` 2 values. It might make sense to sacrifice spec
conformance in favor of faster implementation (or provide 2 versions).

Another thing to note is that `document.querySelectorAll` returns static
NodeList, while `document.getElementsByClassName` is specified to return
(and does return in current implementations) live NodeList. Such
discrepancies could lead to bugs and confusion.

Any other oversights on my part?

Another branch for the current document.getElementsByClassName.
Certainly it can be found in more browsers than QSA.

For many applications, a simple loop through the results of a gEBTN
wrapper trumps the multi-host-method mashup. It will be slower, but
100% consistent and predictable across browsers. Design apps
accordingly.

Your regex looks to be an upgrade of the one I have been using. Of
course, I would advise avoiding class names that would fool the
earlier (and simpler) recommendation.
 
G

Gregor Kofler

Am Wed, 08 Apr 2009 05:08:39 -0700 schrieb Jorge:
(...)

I haven't needed to use such a function in the last year. I recall
there were a number of implementations, by a number of talented
Javascript programmers. Is there any one of these that eventually came
to be seen as the best?

I'd use a "walkTheDom" function:
[snip]

and pass it a proper class-matching f() of your liking: e.g. *not* this
one:

function func (e) {
if (e.className.indexOf(class) >= 0) {
results.push(e);
}
}

The class-matching function (as you noted) needs to be more complex (like
weeding out textNodes). Given that, I suppose this solution (since it
tends to walk over *all* child nodes of a given parent node) will be much
slower than iterating over a collection aquired with gEBTN().

(I'm pretty sure you'll set up a benchmark now....)

Gregor
 
J

Jorge

Am Wed, 08 Apr 2009 05:08:39 -0700 schrieb Jorge:
I'd use a "walkTheDom" function:
[snip]

and pass it a proper class-matching f() of your liking: e.g. *not* this
one:
function func (e) {
  if (e.className.indexOf(class) >= 0) {
    results.push(e);
  }
}

The class-matching function (as you noted) needs to be more complex (like
weeding out textNodes). Given that, I suppose this solution (since it
tends to walk over *all* child nodes of a given parent node) will be much
slower than iterating over a collection aquired with gEBTN().

Yes, but walkTheDom is such a convenient function/tool that every JS
programmer ought to know it well. walkTheDom(target, f) is ~ a
handcrafted target.gEBTN('*').forEach(f) that works.
(I'm pretty sure you'll set up a benchmark now....)

:)
 
J

Jim Ley

Yes, but walkTheDom is such a convenient function/tool that every JS
programmer ought to know it well. walkTheDom(target, f) is ~ a
handcrafted target.gEBTN('*').forEach(f) that works.

Erm, no, it's a function almost no-one should know, as it almost
always takes long enough to do anything that the user notices the
freeze up.

Anything that iterates over large numbers of nodes in the DOM is
flawed in the single-threaded javascript envrionment.

freezing screws up the user. Do not do it, definately do not place
the tools in the hands of simpletons.

Jim.
 
J

Jorge

Erm, no, it's a function almost no-one should know, as it almost
always takes long enough to do anything that the user notices the
freeze up.

Anything that iterates over large numbers of nodes in the DOM is
flawed in the single-threaded javascript envrionment.

freezing screws up the user.  Do not do it, definately do not place
the tools in the hands of simpletons.

That might be true 10 years ago and when walking pretty large trees.
But not today with today's CPUs and today's JS engines. Don't you
think so ?
 
J

Jim Ley

That might be true 10 years ago and when walking pretty large trees.
But not today with today's CPUs and today's JS engines. Don't you
think so ?

I have a T61p thinkpad, so a pretty modern machine with a CPU of
today, using late version browsers, and walking the DOM is very
noticeable the pauses drive me crazy.

So yes, I think it's still very much an issue.

Jim.
 
J

Jorge

What's a simpleton ?
I have a T61p thinkpad, so a pretty modern machine with a CPU of
today, using late version browsers, and walking the DOM is very
noticeable the pauses drive me crazy.

So yes, I think it's still very much an issue.


Look, these are the times to walk a tree of 1288 (<span>) elements.
Kofler is right in that as walkTheDom walks into all kinds of nodes
it's a bit slower. But I doubt very much that delays of between a
couple ms and a couple of tens of ms (in the worst case) could drive
nobody crazy. Unless your code walksTheDOM viciously or the trees it
walks are insanely large.


Chrome 1.0.154.53 on Windows NT
walkTheDomClassic 271 (3.69ms)
walkTheDomNew 450 (2.22ms)

Safari 3.2.1 on Windows NT
walkTheDomClassic 209 (4.78ms)
walkTheDomNew 301 (3.32ms)

Opera 9.64 on Windows NT
walkTheDomClassic 151 (6.62ms)
walkTheDomNew 320 (3.13ms)

MSIE 8.0 on Windows NT
walkTheDomClassic 28 (35.71ms)
walkTheDomNew 56 (17.86ms)

Firefox 3.0.6 on Windows NT
walkTheDomClassic 58 (17.24ms)
walkTheDomNew 103 (9.71ms)



Safari 4.0 on Intel Mac OS X 10_5_6
walkTheDomClassic 280 (3.57ms)
walkTheDomNew 299 (3.34ms)

Firefox 2.0.0.20 on Intel Mac OS X
walkTheDomClassic 13 (76.92ms)
walkTheDomNew 33 (30.3ms)

Firefox 2.0.0.9 on Intel Mac OS X
walkTheDomClassic 13 (76.92ms)
walkTheDomNew 31 (32.26ms)

Opera 9.64 on Intel Mac OS X
walkTheDomClassic 65 (15.38ms)
walkTheDomNew 103 (9.71ms)

Firefox 3.0.8 on Intel Mac OS X 10.5
walkTheDomClassic 38 (26.32ms)
walkTheDomNew 69 (14.49ms)


The test code is:

function walkTheDomClassic (node, f) {
f(node);
node = node.firstChild;
while (node) {
walkTheDomClassic(node, f);
node = node.nextSibling;
}
}

function walkTheDomNew (node, f) {
var set= node.getElementsByTagName('*');
var length= set.length;
while (length--) {
f(set[length]);
}
}

var flipFlop= true;
function f (e) {
if (e.style && e !== container) {
e.className= (flipFlop= !flipFlop) ? "" : "bold";
}
}

http://jorgechamorro.com/cljs/052/
 
L

Lawrence Krubner

Of course. `isHostMethod` would undoubtedly be safer than boolean type
conversion.


Err. Overwriting `document.getElementsByClassName` was a clear mistake
on my part. That should have been just `getElementsByClassName` of course..

I tried following specs here, but all this stripping and tokenizing
seems like an overkill. I, for one, can't remember giving
`getElementsByClassName` 2 values. It might make sense to sacrifice spec
conformance in favor of faster implementation (or provide 2 versions).

Another thing to note is that `document.querySelectorAll` returns static
NodeList, while `document.getElementsByClassName` is specified to return
(and does return in current implementations) live NodeList. Such
discrepancies could lead to bugs and confusion.


What is the difference between static NodeList and live NodeList?
 
L

Lawrence Krubner

Further to what Thomas said, the method usually returns a collection
or array of element references and therefore is typically given a
plural name of getElementsByClassname.
It has been implemented in a couple of browsers and various libraries,
however browser support will likely be obviated by the W3C Selectors
API:

which offers a much wider variety of selectors than just class.

Which we can actually take advantage of right now (e.g. in IE8, which
lacks `document.evaluate`) when implementing `getElementsByClassName`.

I'm thinking about something along these lines (completely untested and
based on HTML5 *draft*):

(function(){
   if (document.querySelectorAll) {

     // [1]
     var sWsp = '[\\u0020\\u0009\\u000A\\u000C\\u000D]';

     var reWsp = new RegExp(sWsp);
     var reTrim = new RegExp('^' + sWsp + '+|' + sWsp + '+$');

     // [2]
     document.getElementsByClassName = function(className) {
       // trim
       if (className = String(className).replace(reTrim, '')) {

         // split by tokens, make css expr e.g. "foo bar" -> ".foo.bar"
         var cssExpr = '.' + className.split(reWsp).join('.');
         return document.querySelectorAll(cssExpr);
       }
       return [ ];
     }
   }

})();

[1]http://www.whatwg.org/specs/web-apps/current-work/#space-character
[2]http://www.whatwg.org/specs/web-apps/current-work/#dom-document-getel....


Hmmm, I'd never heard of querySelectorAll() before. Did a quick search
for it. This is currently only supported in FireFox, yes?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top