0

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...

4
  • I won't spend too much time analyzing this because you didn't post a complete example. However, try changing the getElementsByClassName calls to document.querySelectorAll(".Selected") instead. This avoids "live list" problems, which can come up with altering the class name of elements and when mutating the DOM structure, both of which you're doing. Commented Mar 29, 2018 at 16:50
  • Also, you have onclick="select(this)", but the function is defined as function select(ele,eve){, but then you're using the global event instead of eve anyway, so you're going to run into problems there eventually. Commented Mar 29, 2018 at 16:52
  • @CrazyTrain That was it. I had thought that changing the list as I iterated through it might be an issue, but I did not know that querySelector avoided this issue. Thank you for the help. If you put this as an answer I will accept it. Commented Mar 29, 2018 at 16:56
  • Yeah, querySelectorAll provides a static list, so it isn't affected by changes made to the DOM. I'll post an answer in a minute. Commented Mar 29, 2018 at 16:56

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.