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


(function(childNodes){

/* Another (C) WebReflection Silly Idea */

// how to re-use them all, just an example
if(!document.getElementsByClassName)
document.getElementsByClassName = function getElementsByClassName(className){
for(var
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
})(document.getElementsByTagName("*"));

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?

13 comments:

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.

Regards

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.

Massimo

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.