Jessica
Jessica

Reputation: 9830

Add array of elements to dom

I have 5 div's created in HTML, and I want to add all of them into a div wrapper I created in JavaScript. I tried looping through the 5 div's via a for-in loop, then append the div as a child of the wrapper.

For some reason, the for loop changes the 5 div's order and doesn't append all of them in wrapper. How can I add all div's to wrapper, keeping the (HTML) order, using JavaScript?

(Please don't post JQuery answers because that isn't the question. I want JavaScript answers only.)

JSFiddle

var wrapper = document.createElement('div'),
  myClass = document.getElementsByClassName('myClass');

myClass[0].parentElement.appendChild(wrapper);
wrapper.id = 'wrapper';

for (var key in myClass) {
  if (!myClass.hasOwnProperty(key)) continue;

  wrapper.appendChild(myClass[key]);
}
#wrapper {
  border: 2px solid green;
  color: brown;
}
<div class="myClass">First</div>
<div class="myClass">Second</div>
<div class="myClass">Third</div>
<div class="myClass">Fourth</div>
<div class="myClass">Fifth</div>

Upvotes: 7

Views: 13276

Answers (5)

Iglesias Leonardo
Iglesias Leonardo

Reputation: 544

The simplest is to use append method from Element and use it like:

Element.prototype.append.apply(domElement, arrayOfElements);

Upvotes: 0

Rogier Spieker
Rogier Spieker

Reputation: 4187

The document.getElementsByClassName method returns an HTMLCollection-object, which is similar to an array, as in it has numeric keys which should be used.

e.g. for (var i = 0; i < myClass.length; ++i)

Once you use an incremental numeric index, you'll notice it actually behaves the same as your key in myClass, which is rather logical, as the key is the numeric index.

What is happening is that an HTMLCollection represents elements in document order (a so called live list, which reflects the changes in the DOM) and you are moving them around by appending them to the wrapper element (hence the order within the HTMLCollection changes too).

There are several tricks to work around this, the one closest to your current setup would be to walk through the HTMLCollection from end to start and insertBefore instead of appendChild

for (var len = myClass.length - 1; len >=0; --len) {
    wrapper.insertBefore(myClass[len], wrapper.firstChild);
}

insertBefore fiddle

This works because the wrapper is (in your example) after the elements you're moving into it, therefor not changing the order of the elements.

There is another (easier) approach: document.querySelectorAll. The querySelectorAll method returns a (static) NodeList, so you can safely assume the order will not change while you move nodes around.

The syntax is (IMHO) more convenient than getElementsByClassname, as it uses CSS Selectors (much like the popular javascript framework we won't mention)

querySelectorAll fiddle

Upvotes: 6

Maciej Kwas
Maciej Kwas

Reputation: 6419

You are changing the collection on the fly (in the loop itself) by removing items from it, that's why it acts wired. Here's the code that should actually work:

var wrapper = document.createElement('div'),
myClass = document.getElementsByClassName('myClass'),
myClassParent = myClass[0].parentNode;

while (myClass.length) {
    wrapper.appendChild(myClass[0]);
}
myClassParent.appendChild(wrapper);
wrapper.setAttribute('id','wrapper');

https://jsfiddle.net/byd9fer3/1/

Upvotes: 1

Ting Shun Ng
Ting Shun Ng

Reputation: 26

I have a simple working version for you with code. Thanks

<html>
<body>

<button onclick="myFunction()">Try it</button>

<p><strong>Note:</strong> The getElementsByClassName() method is not supported in Internet Explorer 8 and earlier versions.</p>

<div class="myClass">First</div>
<div class="myClass">Second</div>
<div class="myClass">Third</div>
<div class="myClass">Fourth</div>
<div class="myClass">Fifth</div>

<script>
function myFunction() {

    var wrapper = document.createElement('div');
    var x = document.getElementsByClassName("myClass");
    

for(var i=0; i < x.length;++i){
    var newdiv = document.createElement('div');
    newdiv.appendChild(document.createTextNode(x[i].innerHTML));
    wrapper.appendChild(newdiv);
   }

 document.body.appendChild(wrapper);
}
</script>

</body>
</html>

Upvotes: 0

Gavriel
Gavriel

Reputation: 19237

Look at your for loop step by step:

1. myClass[First, Second, Third, Fourth, Fifth]; wrapper[] ; key = 0
2. myClass[Second, Third, Fourth, Fifth] ; wrapper[First]; key = 1

Now instead of Second, you'll take Third, because key is 1, but you'll need to take the item at index 0. This also gives the fix: always take the item at position 0.

var wrapper = document.createElement('div'),
  myClass = document.getElementsByClassName('myClass');

myClass[0].parentElement.appendChild(wrapper);
wrapper.id = 'wrapper';

for (var i = 0; i < myClass.length; i++) {
  wrapper.appendChild(myClass[0]);
}
#wrapper {
  border: 2px solid green;
  color: brown;
}
<div class="myClass">First</div>
<div class="myClass">Second</div>
<div class="myClass">Third</div>
<div class="myClass">Fourth</div>
<div class="myClass">Fifth</div>

Upvotes: 3

Related Questions