Friday, November 23, 2007

Different type returned by getElementsByTagName in Safari, Others

The Javascript function getElementsByTagName recently gave me cross-browser grief. I had a web page with a bunch of checkboxes (a type of input element) and wanted to iterate over the ones with a certain class. So I wrote this:

var nodes = document.getElementsByTagName('input')
for(i in nodes)
{
if(nodes[i].className == 'aCertainClass')
; // process checkbox
}
This worked fine in Firefox 2 and IE 6. Later I started testing with Safari for Windows and noticed a problem. I tracked it down to this discovery: getElementsByTagName returns different types in Firefox and Safari.

In Firefox, getElementsByTagName returns an HTMLCollection, which works fine with the iterator style for(i in nodes). In Safari, it returns a NodeList, and iterating with for(i in nodes) takes you on a trip through the object's methods, rather than its collection contents.

The fix for this was to change the iteration style, since both HTMLCollection and NodeList support the length field:

var nodes = document.getElementsByTagName('input')
for(var i=0; i < nodes.length; i++)
{
// same as before
}
For what it's worth, I've only seen NodeList documented as the expected return type from getElementsByTagName. Why it doesn't work with the for(i in nodes) iteration syntax is something for which I'd like an explanation.

3 comments:

Anonymous said...

Yeap! I just encountered this little inconsistency with Safari a minute ago :)! Thanks for sharing your discovery! Now I know Safar treats the for(x in y) statement differently.

JFKBits said...

gabe e: Great to hear this helped! Several people a week hit this post from a web search, so it seems to be a common issue. Since I like the Firefox behavior it's hard to see this getting fixed, because I think it would be harder to convince Apple to change than Mozilla. But it'd be nice to see this become consistent.

Srinivas Chamarthi said...

Hi, I am experiencing some problem related to safari browser compatability.

However this works in FF2 but not in SAFARI 3.x

var select = $d.getElementById(ref.container,"leaf_1072","tbody"); // if I hardcode its working here

var select = $d.getElementById(ref.container, "leaf_"+id.split("_")[1],"tbody"); // if I pass id its not working

I had alerts and both confirm have same value

"leaf_"+id.split("_")[1] = "leaf_1072"

can you point out whats wrong with my code ?