jeffpkamp
jeffpkamp

Reputation: 2866

Javascript for loop appendChild does not append all items

I am moving list elements in a class from one list to another, but I am getting extremely odd behavior.

The relevant Code is found in these functions:

function moveright(){
            console.log("things that are selected");
            selected=document.getElementsByClassName("Selected");
            for (x=0;x<selected.length;x++){
                    console.log(selected.item(x).innerText);
            }
            list=document.getElementById("rightlist");
            console.log("Moving right");
            for (x=0;x<selected.length;x++){
                    list.appendChild(selected.item(x));
                    console.log(selected.item(x).innerText);
            }
            console.log("things that moved right");
            for (x=0;x<list.childElementCount;x++){

                    console.log(list.children.item(x).innerText);
            }
    }

function select(ele,eve){
                if (!(event.shiftKey)){
                        selected=document.getElementsByClassName("Selected");
                        for (x=0;x<selected.length;x++){
                                selected[x].className=selected[x].className.replace(" Selected","");
                        }
                }
                if (ele.className.indexOf(" Selected") == -1) {
                        ele.className=ele.className+" Selected";
                }
        }

An example of a test element:

<li style="user-select:none" onclick="select(this)" path="./this/is/a/path" class="pft-file ext-txt Selected">test2.txt</li>

rightlist and leftlist are just <ul> elements. When I select three items and execute the moveright function this the console output, which corresponds with what happens on the screen:

things that are selected
test1.txt
test2.txt
test3.txt
Moving right
test2.txt
test1.txt
test3.txt
things that moved right
test1.txt
test3.txt

When I do the same experiment with 2 elements, it still leaves one behind. When I call the function a second time, the last element moves to the rightlist. When I call an identical function to move the elements to the leftlist from the rightlist, it works fine. I'm at my wits end on this one.

EDIT So a little bit of a clue as to what is going on, when I make the list longer, it leaves behind every other item, so any items in the 1,3,5 positions are left and the 0,2,4 positions are taken...

Upvotes: 0

Views: 76

Answers (1)

user2437417
user2437417

Reputation:

Your issue is that document.getElementsByClassName returns a "live list", which means that elements can disappear from the list and appear into it based on changes you make to the DOM.

For example, if you select by class name "foo", but then remove that class from one of the elements selected, the element gets removed from the list. If you add the "foo" class to an element, that element gets added to the list. Same goes with removing entire elements from the DOM that were in the collection.

So doing such mutations can have surprising effects, especially in a loop. It's basically the same problem of mutating an Array's structure while you're iterating it.

As a fix, use .querySelectorAll to select the elements. This returns a static list of elements, which will be unaffected by changes in the DOM. The elements themselves will see their own changes of course, but the list of elements won't change.

Upvotes: 2

Related Questions