Thursday, June 08, 2006

JavaScript - Concatenating NodeLists

In JavaScript, you cannot concatenate two nodeLists using Array.concat(). This is because nodeLists are not true Arrays, they are more like Objects that happen to have a length property. Here's an example scenario:

var pElements = document.body.getElementsByTagName("p");
var divElements = document.body.getElementsByTagName("div");

// This results in a new array where the last element is the entire
divElements nodeList
var pAndDivElements = pElements.concat(divElements);

As a workaround, I wrote a quick method that will join two nodelists:

joinNodeLists: function(list1,list2){
var i = list2.length - 1;

do{ list1[list1.length] = list2[i];

}
while(i--);

return list1;
}


Note that I use list1[list1.length] = list2[i] instead of list1.push(list2[i]). This is for compatibility with IE 5.

To Do: Make a function that accepts an unlimited number of nodeLists and joins them all.

Was this post helpful to you? If so, please consider making a small donation to keep this blog going.

8 Comments:

Anonymous Anonymous said...

Thanks for the function ... exactly what I needed.

12:26 PM  
Anonymous Anonymous said...

Hmmm, Thanks very much; works just like it says on the tin. However why cant I inline the code?

i=doc.getElementsByTagName("input");
t=doc.getElementsByTagName("textarea");
var ie=i.length -1;
do { t[t.length]=i[ie]; } while (ie--);
alert(t.length);

I only need the routine once and it seemed simple enough to inline. But it does not work. Why not?

Rgds Fergus

2:02 PM  
Blogger Unknown said...

Um, have you checked to make sure this actually works? NodeLists are readonly, so what I found when I ran your function was that the length of the nodelist wouldn't update on subsequent loop iterations, meaning new items clobbered previous ones. I solved this by declaring an array and pushing the items onto that. Unlike the nodelist, the array length will update as you add new elements. I also wrote a version that accepts an indefinite number of nodelist arguments:


function joinNodeLists() {

if (!arguments.length) {
return;
}

var newList = [];
for (var i = 0; i < arguments.length; i++) {
var list = arguments[i];
for (var j = 0; j < list.length; j++) {
// Don't use push() for IE 5 compatibility
// newList.push(list[j]);
newList[newList.length] = list[j];
}
}

return newList;
}

2:02 PM  
Blogger tom said...

Interesting - what browser(s) are you testing in? (To answer your first question: yes, I do test my code :P ) I wonder if what you're seeing has anything to do with the problems anonymous had when he tried to take the code out of the context of a function.

2:11 PM  
Blogger Unknown said...

I only tested this in Firefox and found it didn't work. Using Firebug's debugger I could see the failure of the nodelist's length property to update as items were concatenated.

10:24 AM  
Blogger Unknown said...

I retract my remarks. It turns out the error was my code that called joinNodeLists. Sorry about that. So to handle an indefinite number of nodeList arguments, you can just have:

function joinNodeLists(){

if (!arguments.length) {
return null;
}
var newList = arguments[0];
for (var i = 1; i < arguments.length; i++) {
var list = arguments[i];
for (var j = 0; j < list.length; j++) {
// Don't use push() for IE 5 // compatibility
newList.push(list[j]);
newList[newList.length] = list[j];
}
}

return newList;
}

8:41 AM  
Blogger tom said...

Hi Rebecca,

Glad you got it sorted out. Looking at your code, it seems there's a comment missing:

// Don't use push() for IE 5 // compatibility
newList.push(list[j]);
newList[newList.length] = list[j];

Shouldn't the newList.push(list[j]) line be commented out?

9:37 AM  
Anonymous Anonymous said...

It didn't work in firefox. This does:

function joinNodeLists(){

if (!arguments.length) {
return null;
}
var newList = new Array();
for (var i = 0; i < arguments.length; i++) {
var list = arguments[i];
for (var j = 0; j < list.length; j++) {
// Don't use push() for IE 5 // compatibility

newList[newList.length] = list[j];

}
}

return newList;
}

3:09 PM  

Post a Comment

<< Home