简体   繁体   中英

Javascript for loop not working properly?

I'm having an issue with a function I've written to "clean" up, see the code below and I'll explain how it works underneath.

clean: function (e) {
    var
            els = null,
        i = 0;

    if (e === undefined) {
        e = this.cont;
    }

    els = e.getElementsByTagName('*');

    for (i=0;i<els.length;i++) {
        if (els[i].className.search('keep') === -1) {
            e.removeChild(els[i]);
        }
    }
    return this;
},

The argument e is a dom element, if it isn't supplied this.cont is also a dom element stored earlier in the whole function and e is defaulted to it.

The function loops through all of the child elements and checks it doesn't have the class keep (fairly obvious what this does) and removes any that don't match.

It all seemed to be working but I have an element which has 2 images and 2 inputs none with the class 'keep' but the variable i only gets to 2 and the loop stops (it should reach 4 and remove all four elements)

any help would be greatly appreciated.

/* UPDATE */

Thanks to @pimvb and and @Brett Walker the final code which works great is below.

clean: function (e) {
    var
        els = null,
        i = 0;

    if (e === undefined) {
        e = this.cont;
    }

    els = e.getElementsByTagName('*');

    i = els.length;

    while (i--) {
        if (els[i].className.search('keep') === -1) {
            els[i].parentNode.removeChild(els[i]);
        }
    }

    return this;
},

The .getElementsByTagName function returns a NodeList which is basically an array but is 'live', which means it's updated automatically if you eg remove a child. So when iterating, els.length is changing, resulting in being 2 when you remove 2 children (there are 4 - 2 = 2 left). When having removed 2 children, i == 2 so the loop will end prematurely to what you expect.

To circumvent this and make it a 'static' array, you can convert it into an array like this, which does not update itself:

els = [].slice.call(e.getElementsByTagName('*')); // [].slice.call is a trick to
                                                  // convert something like a NodeList
                                                  // into a static, real array

As Brett Walker pointed out, you can also iterate backwards, like this:

http://jsfiddle.net/pimvdb/cYKxU/1/

var elements = document.getElementsByTagName("a"),
    i = elements.length;

while(i--) { // this will stop as soon as i == 0 because 0 is treated as false
    var elem = elements[i]; // current element

    if(elem.className == "test") // remove if it should be removed
        elem.parentNode.removeChild(elem);
}

This will start at the last element. The .length still gets updated (ie becomes less), but this does not matter as you only used it at the beginning, and not during iterating. As a result, you don't suffer from this 'quirk'.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM