Search code examples
javascriptwhile-loopinfinite-loopappendchild

Why is this while loop infinite? JavaScript appendChild


I swear this was just working fine a few days ago...

elm = document.querySelectorAll(selector);
var frag = document.createDocumentFragment();
while (elm[0]){
    frag.appendChild(elm[0]);
}

Right, so, this should append each node from our elm node list. When the first one is appended, the second "moves" to the first position in node list, hence the next one is always elm[0]. It should stop when the elm nodeList is completely appended. However, this is giving me an infinite loop. Thoughts?

EDIT - because I've gotten the same answer several times... A nodeList is not an array, it is a live reference. When a node is "moved" (here, appended) it should be removed automatically from the node list. The answers all saying "you're appending the same element over and over" - this is what's happening, it shouldn't be. A for loop shouldn't work, because when the first node is appended, the next node takes its index.

2nd EDIT

So the question is now "why is the nodeList behaving as an array?". The node list SHOULD be updating every time a node is being appended somewhere. Most peculiar.

Solution (in case someone needs something to handle live + non-live node lists)

elm = (/*however you're getting a node list*/);
var frag = document.createDocumentFragment();
var elength = elm.length;
for (var b = 0; b<elength; b++){
    if (elm.length === elength){
        frag.appendChild(elm[b]);
    } else {
        frag.appendChild(elm[0].cloneNode());
    }
}

Basically, just checking to see if the node list has changed length.


Solution

  • From the MDN Docs

    Element.querySelectorAll

    Summary

    Returns a non-live NodeList of all elements descended from the element on which it is invoked that match the specified group of CSS selectors.

    Syntax

    elementList = baseElement.querySelectorAll(selectors);
    

    where

    • elementList is a non-live list of element objects.
    • baseElement is an element object.
    • selectors is a group of selectors to match on.

    From the docs above you can see it does not automatically remove it when you append it to another element since it is non live. Run a demo to show that feature.

    var selector = "div";
    elm = document.querySelectorAll(selector);
    var frag = document.createDocumentFragment();
    console.log("before",elm.length);
    frag.appendChild(elm[0]);
    console.log("after",elm.length);
    

    When the code above runs, in the console you get.

    before    3
    after     3
    

    If you want to do the while loop, convert to an array and shift() the items off

    var selector = "div";
    var elmNodeLIst = document.querySelectorAll(selector);
    var frag = document.createDocumentFragment();
    var elems = Array.prototype.slice.call(elmNodeLIst );
    while (elems.length) {
        frag.appendChild(elems.shift());
    }
    console.log(frag);