views:

196

answers:

6

I'm making a bookmarklet, but I've encountered some wierd behaviour in IE8. The code causing the problem is this:

var els = document.getElementById("my_id").getElementsByTagName("*");
for(var i in els)
{
    alert(i+","+els[i])
}

The first thing that is alerted is "length, n". This isn't the case in chrome: just in IE8.

Interestingly, it seems to behave differently depending on whether the code goes in the console/address bar or the page itself.

Is this standard behaviour?

EDIT:

Not down to the website that I run it on either. Is it possible that getElementsByTagName returns an array with a "length" key set in IE? It certainly doesn't return a pure array.

+3  A: 

What you get is not an array, it is a nodeList. See here, click the link "getElementsByTagName('element_name')"

Furthermore, for..in is not meant to be used for iterating over an array anyway. Use

var item;
for (var idx=0, len=arr.length; idx<len; ++idx) {
  item = arr[idx];
  // whatever
}

for array iteration.

For a nodelist, you can get the element with

list.item(idx); // where list is what you got from getElementsByTagName

if you want to do it "right".

npup
I'm pretty sure you want `idx++` instead of `++idx`. Otherwise you miss the first element.
Josh Stodola
No, that's fine. `idx` is 0 until the *last* part of the for-expression is evaluated. Which is `++idx`.Here's the order: 1: `var idx=0, len=arr.length` 2: `idx<len` 3: {code of the body} 4: `++i` So `++idx` or `idx++` doesn't matter for that reason.
npup
@npup Thanks for clarifying ;) I guess I am just so used to seeing i++ that this looked foreign to me!
Josh Stodola
In fact ++idx is slightly better because it updates idx. idx++ clones idx. In practice though it probably doesn't matter much
plodder
+1  A: 

The IE behavior is correct. It may return all kinds of weird stuff, since for .. in iterates over all member names of the object.

getElementsByTagName returns a NodeList, and the properties of a NodeList are specified in the DOM standard: length and item(i). Most JavaScript implementations will also allow [i].

Therefore, you should write the following:

var els = document.getElementById("my_id").getElementsByTagName("*");
for (var i = 0;i < els.length;i++)
{
    alert(i+","+els.item(i));
}
phihag
+1  A: 

Do not use for..in for anything other than enumerating properties of an object (unless you like surprises!) Just use a simple for loop for arrays and nodelists.

Chetan Sastry
+1  A: 

Here's how I iterate over lists. This way, you won't have to write an extra line in the loop, such as var el = els[i].

var els = document.getElementById("my_id").getElementsByTagName("*");
for (var i=0, el; el=els[i]; i++) {
    // do what you like here
}
mojbro
A: 

Not unless you define your own iterator for NodeLists, which is JavaScript (Mozilla)-only. [Here's an implementation] of one I have created for my js-iterators repo.

Eli Grey
A: 

A method to convert an object, like a node list, arguments object, or custom object to an array of its members is often useful.

Array.from= function(what){
 var L, A= [];
 if(!what) what= {};
 L= what.length;
 if(typeof L== 'number'){
  while(L) A[--L]= what[L];
  return A;
 }
 for(var p in what){
  if(what.hasOwnProperty(p)) A[A.length]= what[p];
 }
 return A;
}

var N=Array.from(document.getElementById("my_id").getElementsByTagName("*"))
while(N.length){
tem=N.shift();
}
kennebec