13

I'm attempting to merge two arrays made up of html objects. For some reason using .concat() will not work for me.

Here's a simple pen to demonstrate the problem: http://codepen.io/anon/pen/kIeyB

Note: I tried searching for something remotely similar but found nothing that answered my question.

I figure you can do this the ole fashion way using for-loops but I rather not re-invent the wheel.

var x = document.getElementById("hello");
var items = x.getElementsByClassName("one");
//alert(items.length);
var items2 = x.getElementsByClassName("two");
//alert(items2.length);
items = items.concat(items2);
//alert(items.length);
jfriend00
  • 683,504
  • 96
  • 985
  • 979
user3718546
  • 323
  • 4
  • 12

4 Answers4

12

items and items2 are nodeList or HTMLCollection objects, not arrays. They do not contain a .concat() method. They have a .length property and support [x] indexing, but they do not have the other array methods.

A common workaround to copy them into an actual array is as follows:

// convert both to arrays so they have the full complement of Array methods
var array1 = Array.prototype.slice.call(x.getElementsByClassName("one"), 0);
var array2 = Array.prototype.slice.call(x.getElementsByClassName("two"), 0);
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thank you, this works perfectly! Out of curiosity, could you explain the slice.call() and what the '0' parameter is for. – user3718546 Jun 10 '14 at 05:35
  • @user3718546: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice. – Abhitalks Jun 10 '14 at 05:41
  • @user3718546 - `.slice()` works with anything that has a `.length` and supports `[i]` indexing from `0` to `length - 1` (which includes a `nodeList`, and `HTMLCollection, and `arguments` object, etc...). So, we're using the `.slice()` method from `Array.prototype` and calling that method, but passing it a `nodeList` as the object to operate on with `.call()`. The `0` is the first argument to `.slice()` which means to start the shallow copy at the beginning. If you read the doc for both `.slice()` and `.call()`, you will see how they work together. – jfriend00 Jun 10 '14 at 06:00
5

This can be also be done like this:

var allitems = [];
allitems = Array.prototype.concat.apply(allitems, x.getElementsByClassName("one"));
allitems = Array.prototype.concat.apply(allitems, x.getElementsByClassName("two"));

The allitems variable will be a single javascript Array containing all elements with class one & two.

Avi Tevet
  • 778
  • 1
  • 7
  • 13
1

document.getElementsByClassName doesn't return an array. It returns NodeList which has length property.

Arthur
  • 3,056
  • 7
  • 31
  • 61
Jeetendra Chauhan
  • 1,977
  • 2
  • 15
  • 21
1

What you have are HTMLCollections, which although behave like arrays, but are not arrays. See here: https://developer.mozilla.org/en/docs/Web/API/HTMLCollection:

..A collection is an object that represents a lists of DOM nodes..

In your case, you could concatenate these objects together into a new array:

var itemsnew;
var x = document.getElementById("hello");
var items = x.getElementsByClassName("one");
var items2 = x.getElementsByClassName("two");
itemsnew = Array.prototype.concat.call(items, items2);

Now, if you:

console.log(itemsnew);

Will return:

[HTMLCollection[1], HTMLCollection[1]]

And:

console.log(itemsnew[0][0]);

Will return:

<div class="one"></div>
Abhitalks
  • 27,721
  • 5
  • 58
  • 81
  • 1
    Thank you for your answering and everything you said was correct. For my situation; however, I was trying to concatenate multiple arrays so that I could then transverse them in one loop. Having a multi-dimensional array like you proposed wouldn't be the most appropriate and most useful approach for users trying to manipulate multiple nodeLists. – user3718546 Jun 10 '14 at 05:33