Go Back   CodingForums.com > :: Client side development > JavaScript programming > Post a JavaScript

Before you post, read our: Rules & Posting Guidelines

Reply
 
Thread Tools Rate Thread
Enjoy an ad free experience by logging in. Not a member yet? Register.
Old 12-22-2008, 11:19 AM   PM User | #1
Kor
Red Devil Mod


 
Kor's Avatar
 
Join Date: Apr 2003
Location: Bucharest, ROMANIA
Posts: 8,478
Thanks: 58
Thanked 379 Times in 375 Posts
Kor has a spectacular aura aboutKor has a spectacular aura about
document.getElementsByClassName()

FF3, Chrome and Opera 9+ have already implemented this method : document.getElementsByClassName() as javascript native, but for a crossbrowser approach, you may use this workaround:
Code:
<script type="text/javascript">
onload=function(){
if(!document.getElementsByClassName){
document.getElementsByClassName=function(cn){
var allT=document.getElementsByTagName('*'), allCN=[], i=0, a;
	while(a=allT[i++]){
	a.className==cn?allCN[allCN.length]=a:null;
	}
return allCN
}
}
}
</script>
__________________
KOR
Offshore programming
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Kor is offline   Reply With Quote
The Following 2 Users Say Thank You to Kor For This Useful Post:
abduraooft (12-22-2008), oesxyl (12-22-2008)
Old 12-22-2008, 01:52 PM   PM User | #2
rangana
Senior Coder

 
rangana's Avatar
 
Join Date: Feb 2008
Location: Cebu City, Philippines
Posts: 1,752
Thanks: 65
Thanked 372 Times in 365 Posts
rangana will become famous soon enoughrangana will become famous soon enough
Hi Kor,

It would fail on multiple class names like:
Code:
<span class="class1 class2">Dummy</span>
...how about:
Code:
onload=function(){
if(!document.getElementsByClassName){
document.getElementsByClassName=function(cn){
var rx=new RegExp('\\b'+cn+'\\b');
var allT=document.getElementsByTagName('*'), allCN=[], i=0, a;
	while(a=allT[i++]){
	rx.test(a.className)?allCN[allCN.length]=a:null;
	}
return allCN
}
}
}
__________________
Learn how to javascript at 02geek

The more you learn, the more you'll realize there's much more to learn
Ray.ph
rangana is offline   Reply With Quote
The Following 3 Users Say Thank You to rangana For This Useful Post:
abduraooft (12-24-2008), Kor (12-22-2008), oesxyl (12-22-2008)
Old 12-22-2008, 02:13 PM   PM User | #3
Kor
Red Devil Mod


 
Kor's Avatar
 
Join Date: Apr 2003
Location: Bucharest, ROMANIA
Posts: 8,478
Thanks: 58
Thanked 379 Times in 375 Posts
Kor has a spectacular aura aboutKor has a spectacular aura about
Yes, good ideea.
__________________
KOR
Offshore programming
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Kor is offline   Reply With Quote
Old 12-22-2008, 03:16 PM   PM User | #4
rnd me
Senior Coder

 
rnd me's Avatar
 
Join Date: Jun 2007
Location: Urbana
Posts: 3,455
Thanks: 9
Thanked 466 Times in 450 Posts
rnd me is a jewel in the roughrnd me is a jewel in the roughrnd me is a jewel in the rough
with performance optimizations

i liked rangana's take on kor's slim code.
i've seen many versions of this floating around, and this one is pretty good.

i had some time on my hands, so i thought i could tweak it a bit.
i think that since native code performs about 100X faster than js replacements, we should tweak native replacements as much as possible.

The direct string compare is much faster than a regexp, but as we see from kor's code, limited to single class attribs.

Second best to a direct compare, indexOf performs 3-50X faster than a comparable regExp.
This is of limited use however, because it could match partial substring:
ex: searching for "sample" would match "sampleBold".

what we want is the speed of string methods, and the precision of a regexp.



the solution: have the matching code to only work as hard as needed:

Code:
if (!document.getElementsByClassName) {
  	document.getElementsByClassName = function (cn) {
		var rx = new RegExp("\\b" + cn + "\\b"), allT = document.getElementsByTagName("*"), allCN = [], i = 0, a;
			while (a = allT[i++]) {
			  if (a.className && a.className.indexOf(cn) + 1) {
				if(a.className===cn){ allCN[allCN.length] = a; continue;   }
				rx.test(a.className) ? (allCN[allCN.length] = a) : 0;
			  }
			}
		return allCN;
	}
}
this performs about 10 - 50% (avg ~20) faster than rangana's version, and it finds the same things.


after checking for any className at all,
it uses the medium speed indexOf to determine if it's worth further examination.

if there is some kind of match, a super fast direct compare is tried.
failing the direct compare, sub-matches are eliminated by rangana's regexp.



perhaps someone else can tweak it even further?
__________________
my site (updated 5/13)
STATS (2013/5) HTML5:90.2% MOB:14% IE7:0.5% IE8:8.6% IE9:9.8% IE10:10%
rnd me is offline   Reply With Quote
The Following 2 Users Say Thank You to rnd me For This Useful Post:
Kor (12-22-2008), oesxyl (12-22-2008)
Old 12-22-2008, 03:21 PM   PM User | #5
Kor
Red Devil Mod


 
Kor's Avatar
 
Join Date: Apr 2003
Location: Bucharest, ROMANIA
Posts: 8,478
Thanks: 58
Thanked 379 Times in 375 Posts
Kor has a spectacular aura aboutKor has a spectacular aura about
Pretty good development, good job also rnd me
__________________
KOR
Offshore programming
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Kor is offline   Reply With Quote
Old 12-22-2008, 10:00 PM   PM User | #6
oesxyl
Master Coder


 
Join Date: Dec 2007
Posts: 6,682
Thanks: 436
Thanked 890 Times in 879 Posts
oesxyl is a jewel in the roughoesxyl is a jewel in the roughoesxyl is a jewel in the rough
Quote:
Originally Posted by rnd me View Post
Code:
if (!document.getElementsByClassName) {
  	document.getElementsByClassName = function (cn) {
		var rx = new RegExp("\\b" + cn + "\\b"), allT = document.getElementsByTagName("*"), allCN = [], i = 0, a;
			while (a = allT[i++]) {
			  if (a.className && a.className.indexOf(cn) + 1) {
				if(a.className===cn){ allCN[allCN.length] = a; continue;   }
				rx.test(a.className) ? (allCN[allCN.length] = a) : 0;
			  }
			}
		return allCN;
	}
}
would be any problem of speed and portability with this?
Code:
...
if(a.className && a.className.split(' ').indexOf(cn) > -1){
...
best regards
oesxyl is offline   Reply With Quote
Old 12-22-2008, 10:04 PM   PM User | #7
rnd me
Senior Coder

 
rnd me's Avatar
 
Join Date: Jun 2007
Location: Urbana
Posts: 3,455
Thanks: 9
Thanked 466 Times in 450 Posts
rnd me is a jewel in the roughrnd me is a jewel in the roughrnd me is a jewel in the rough
Quote:
Originally Posted by oesxyl View Post
would be any problem of speed and portability with this?
yes.
Array.indexOf is not supported in ie or any browser without 1.6 array methods...

while it could be added in with 1.5 code, that robs peter to pay paul, and i suspect it would be slower in the end.
__________________
my site (updated 5/13)
STATS (2013/5) HTML5:90.2% MOB:14% IE7:0.5% IE8:8.6% IE9:9.8% IE10:10%

Last edited by rnd me; 12-22-2008 at 10:12 PM..
rnd me is offline   Reply With Quote
Users who have thanked rnd me for this post:
oesxyl (12-22-2008)
Old 12-22-2008, 10:18 PM   PM User | #8
oesxyl
Master Coder


 
Join Date: Dec 2007
Posts: 6,682
Thanks: 436
Thanked 890 Times in 879 Posts
oesxyl is a jewel in the roughoesxyl is a jewel in the roughoesxyl is a jewel in the rough
Quote:
Originally Posted by rnd me View Post
yes.
Array.indexOf is not supported in ie or any browser without 1.6 array methods...

while it could be added in with 1.5 code, that robs peter to pay paul, and i suspect it would be slower in the end.
good to know, . that means Opera, Safari and IE?

http://aptana.com/reference/html/api/Array.html

best regards
oesxyl is offline   Reply With Quote
Old 12-22-2008, 10:22 PM   PM User | #9
rnd me
Senior Coder

 
rnd me's Avatar
 
Join Date: Jun 2007
Location: Urbana
Posts: 3,455
Thanks: 9
Thanked 466 Times in 450 Posts
rnd me is a jewel in the roughrnd me is a jewel in the roughrnd me is a jewel in the rough
Quote:
Originally Posted by oesxyl View Post
good to know, . that means Opera, Safari and IE?
best regards
basically anything besides firefox, which already supports document.getElementsByClassName()...

good link btw...
__________________
my site (updated 5/13)
STATS (2013/5) HTML5:90.2% MOB:14% IE7:0.5% IE8:8.6% IE9:9.8% IE10:10%
rnd me is offline   Reply With Quote
Old 12-22-2008, 10:29 PM   PM User | #10
oesxyl
Master Coder


 
Join Date: Dec 2007
Posts: 6,682
Thanks: 436
Thanked 890 Times in 879 Posts
oesxyl is a jewel in the roughoesxyl is a jewel in the roughoesxyl is a jewel in the rough
Quote:
Originally Posted by rnd me View Post
basically anything besides firefox, which already supports document.getElementsByClassName()...

good link btw...
yes, is good,

I discover also problems with split in safari,

best regards
oesxyl is offline   Reply With Quote
Old 12-23-2008, 01:36 AM   PM User | #11
rangana
Senior Coder

 
rangana's Avatar
 
Join Date: Feb 2008
Location: Cebu City, Philippines
Posts: 1,752
Thanks: 65
Thanked 372 Times in 365 Posts
rangana will become famous soon enoughrangana will become famous soon enough
Okay, now I understand. You are filtering the element for a match (single class name), otherwise (for multiple classname), it'll go to the RegEx.

Saves resources indeed.

Edit:
rnd me, I'm confused, on why go all the hassle of those if's statement.

Why not just:
Code:
if (!document.getElementsByClassName)
{
document.getElementsByClassName = function (cn)
	{
	var rx = new RegExp("\\b" + cn + "\\b"), allT = document.getElementsByTagName("*"), allCN = [], i = 0, a;
	while (a = allT[i++])
		if (rx.test(a.className))
			allCN.push(a);
	return allCN;
	}
}
__________________
Learn how to javascript at 02geek

The more you learn, the more you'll realize there's much more to learn
Ray.ph

Last edited by rangana; 12-23-2008 at 01:41 AM..
rangana is offline   Reply With Quote
Old 12-23-2008, 02:03 AM   PM User | #12
itsallkizza
Senior Coder

 
Join Date: Oct 2008
Location: Long Beach
Posts: 1,196
Thanks: 36
Thanked 164 Times in 164 Posts
itsallkizza will become famous soon enough
He's saying that
Code:
if (rx.test(a.className))
is significantly slower than
Code:
if (cn===a.className)
So he's avoiding RegExp.test() for elements with a single class name.

EDIT: I missed your edit Ignore my post.
__________________
Feel free to e-mail me if I forget to respond ;)
ohsosexybrit@gmail.com

Last edited by itsallkizza; 12-23-2008 at 02:15 AM..
itsallkizza is offline   Reply With Quote
Old 12-23-2008, 04:53 AM   PM User | #13
Kor
Red Devil Mod


 
Kor's Avatar
 
Join Date: Apr 2003
Location: Bucharest, ROMANIA
Posts: 8,478
Thanks: 58
Thanked 379 Times in 375 Posts
Kor has a spectacular aura aboutKor has a spectacular aura about
Quote:
Originally Posted by rnd me View Post
basically anything besides firefox, which already supports document.getElementsByClassName()...

good link btw...
Basically all the new browsers' versions, except, of course, IE support now the native getElementsByClassName() method. At least Opera 9+ and Chrome do so.
__________________
KOR
Offshore programming
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
Kor is offline   Reply With Quote
Old 12-23-2008, 05:01 AM   PM User | #14
rnd me
Senior Coder

 
rnd me's Avatar
 
Join Date: Jun 2007
Location: Urbana
Posts: 3,455
Thanks: 9
Thanked 466 Times in 450 Posts
rnd me is a jewel in the roughrnd me is a jewel in the roughrnd me is a jewel in the rough
lots of discussion calls for further explanation.

i throw in a little general performance tuning strategy to provide a context for what's going on here, and why it matters.

loops are slow.
thus, less work at each iteration equals faster execution.
simple and true.

also, functions that will be commonly used should be as fast as possible. you'll have plenty of opportunity to slow things down later on...


a common task like harvesting tags should be as quick and compatible with existing native interfaces.



consider the four possible outcomes of matching a className to an element:
(in order of likelihood)

1. the tag has no className
2. the tag has a className, but the search term is not contained in the element's className
3. the tag has a className, and the search term is equal to the element's className
4. the tag has a className, and the search term is contained in the element's className ( it's one of many classes represented in the attribute)


we provide the code inside the loop four opportunities to avoid the regexp and bail out early.
these opportunities coincide with the four possible outcomes.


line by line walkthrough:
Code:
while (a = allT[i++]) {
continue advancing to the next one in our stack of all tags, and assign it to "a".

Code:
  if (a.className && a.className.indexOf(cn) + 1) {
this one is critical. most tags do not have classes. the first part of the if statement "if a.className" means that any element without a class will fail, and goto the end of the if block, and thus the end of the loop. right away, most tags fail. bailing early lets us avoid a slow string (.indexOf) or regexp (.test) method execution on something with no class at all.

the second part of the if statement (a.className.indexOf(cn) + 1) performs a similar role, acting as a gate keeper to the slow regexp. if no match if found, indexOf will return -1. adding 1 to that = 0, turning a no-match evaluation into a "falsey" value, and failing the if statement.


if we got to this point we know the element has a class, and that the search term at least partially matches the element's class.


Code:
if(a.className===cn){ allCN[allCN.length] = a; continue;   }
most of the time there will only be one class used, and a direct compare is super fast.
therefore, it is fastest to try to get away with a direct compare.
if it works, which it usually will since most class attributes mean one class, we add the result to the array of matches, and continue the loop from the top.
this skips over the regexp if the fast direct match is successful.


if the direct match fails we know that one of two things is true:
1. we found a match substring, like "tree" in "subtree" for instance.
2. we found the proper class among more than one classes represented in one class attribute, typically in a space-separated list.

this line:
Code:
rx.test(a.className) ? (allCN[allCN.length] = a) : 0;
runs the regexp that can determine precisely the difference between a substring and a "one of many" match.
if it's "one of many", it adds the element to the array of matches and proceed with the next loop iteration.

-------

in general, there is a point of diminishing returns using ifs to avoid work.
you could get to the point that the filtering is more intensive than the workload to be avoided.
in this case, the regexp is sufficiently slow to provide ample time to trade in exchange for a couple of if statements.

in other situations, that's not the case, and it pays to runs a few tests and find the proper balance.
__________________
my site (updated 5/13)
STATS (2013/5) HTML5:90.2% MOB:14% IE7:0.5% IE8:8.6% IE9:9.8% IE10:10%

Last edited by rnd me; 12-23-2008 at 05:05 AM..
rnd me is offline   Reply With Quote
Old 12-23-2008, 05:11 AM   PM User | #15
rnd me
Senior Coder

 
rnd me's Avatar
 
Join Date: Jun 2007
Location: Urbana
Posts: 3,455
Thanks: 9
Thanked 466 Times in 450 Posts
rnd me is a jewel in the roughrnd me is a jewel in the roughrnd me is a jewel in the rough
Quote:
Originally Posted by Kor View Post
Basically all the new browsers' versions, except, of course, IE support now the native getElementsByClassName() method. At least Opera 9+ and Chrome do so.
i was referring to Array.indexOf, not getElementsByClassName, but it's probably the same either way. i know chrome has Array.indexOf...
__________________
my site (updated 5/13)
STATS (2013/5) HTML5:90.2% MOB:14% IE7:0.5% IE8:8.6% IE9:9.8% IE10:10%
rnd me is offline   Reply With Quote
Reply

Bookmarks

Jump To Top of Thread


Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 02:04 AM.


Advertisement
Log in to turn off these ads.