My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Monday, September 21, 2009

One Function To Trap Them All

This is a quick one. This technique could have some side-effect I am not aware about but I've never seen it so far in any library. Talking about LiveMonitor I have successfully tested same concept to create faster implementation of some of my DOM common code, not yet updated in vice-versa.

document.getElementsByClassName Example


/* Another (C) WebReflection Silly Idea */

// how to re-use them all, just an example
document.getElementsByClassName = function getElementsByClassName(className){
re = new RegExp("(?:\\s|^)" + className + "(?:\\s|$)"),
ret = [], i = 0, l = 0, length = childNodes.length, node;
i < length; ++i
if((node = childNodes[i]).nodeType === 1 && re.test(node.className))
ret[l++] = node
return ret;

// let the magic happen

What we have here?
  1. A trapped live object, probably used in 90% of selector libraries, but performed via scope resolution (the document) and a function execution (the getElementsByTagName method) over all nodes ("*")
  2. a theoretically zero performances impact to retrieve again every node, being live there is no need to refresh it, and being trapped run-time, it will contain few nodes during the first execution
  3. a single scope able to create N functions based on one of the most common created HTMLCollection

What do you think?


Anonymous said...

Nice approach. Cool way to write your for loop also.

fearphage said...

I like the thought process here. Good idea.

Andrea Giammarchi said...

@fearphage thanks again for the nodeType suggestion (and the silly typo for the className) :D

Cheers guys

Samer Ziadeh said...

Hey I don't understand the regex

(?:\\s|^) doesn't that mean you're checking for character before the beg. of the line?

Aaron Heckmann said...

Any benchmarks yet?

Andrea Giammarchi said...

Samer, that means look for a space or it starts with and look for a space after or it ends with.
The question mark is to avoid RegExp result cache.

Aaraon, not yet, what I expect is better loop performances, what I need to test, is if a wrapped live collection, the biggest one, slows down somehow the angine a la DOMNodeInserted. I think no, but I am not sure yet.


Andrea Giammarchi said...

OK, no slowdown at all, and actually no benefits as well. The problem is that I am basing my tests with wrong timers, so it's 0 or it's 15/16, I cannot truly understand if I it is worth it. In any case, as document.getElementsByClassName, it's really fast :D

J5 said...

Can you elaborate on the tests you're doing? I don't understand what you mean by "The problem is that I am basing my tests with wrong timers, so it's 0 or it's 15/16"

Andrea Giammarchi said...

J% I am taking the classic W3C page used in Taskspeed and others tests.

I put the function on the top, zero differences.

Then I perform a couple of searches, and I take time for each search.

Under windows, timers are almost useless, cause the result for a call that took 8 ms, for example, will always e 15 or 16, and never 8. This problem has been explained in John Resig blog few months (years?) ago :)

Nicolas said...

I like your approach very much, thanks for sharing :)

Anonymous said...

It doesn't behave like a live collection... At least on IE8 SM. And it seems obvious to me, since ret is nothing more than an array.
Am I missing something?

If you want something like a static node list, you can check for querySelectorAll, which is provided by IE8 and should be a lot faster.


Andrea Giammarchi said...

you are missing the childNodes variables ... please read again, test, understand, and ask more if necessary. Regards

MaxArt said...

Sorry to post again after 4 months...
Yes, childNodes is indeed a live NodeList, but ret is just an Array. And we're just adding the matching elements to that array. So, the returned object is not a live list.

That's what I wanted to say.
IE8 still doesn't implement getElementsByClassName but implements querySelectorAll, so if you want to replicate getElementsByClassName you can use querySelectorAll instead, which return a non-live node collection.

I understand that's not the point of your idea, which is to create a scope where you can define every function you want that can make a good use of the live NodeList of all the elements.
So defining getElementsByClassName was just an example, I guess.