When trying to remove options from select, there is always one left, why?
<select id="form-select">
<option>111</option>
<option>222</option>
<option>333</option>
</select>
This JS is not working:
var t = document.querySelector('#form-select');
for(var i of t.options) {
t.remove(i.index)
}
And this is not working also:
for(var i of document.querySelector('#form-select').options) {
i.remove()
}
I know that there are other solutions to achieve that, but i would like to understand why it's not working as it supposed to
When trying to remove options from select, there is always one left, why?
<select id="form-select">
<option>111</option>
<option>222</option>
<option>333</option>
</select>
This JS is not working:
var t = document.querySelector('#form-select');
for(var i of t.options) {
t.remove(i.index)
}
And this is not working also:
for(var i of document.querySelector('#form-select').options) {
i.remove()
}
I know that there are other solutions to achieve that, but i would like to understand why it's not working as it supposed to
Share Improve this question asked Dec 25, 2019 at 23:29 PiotrPiotr 1,93719 silver badges25 bronze badges4 Answers
Reset to default 10 +50The .options
collection is (unfortunately) live, so iterating over the live collection's items one-by-one and .remove
ing every one will result in every odd one being kept. (Eg, right when you remove the first item, the [0]
th item of the collection will immediately become the next item in the collection - what used to be [1]
will become [0]
(and then once you go to the next index at [1]
, the new item at position 0 won't be iterated over)
Use document.querySelectorAll
instead, which returns a collection which is static:
for (const option of document.querySelectorAll('#form-select > option')) {
option.remove();
}
<select id="form-select">
<option>111</option>
<option>222</option>
<option>333</option>
</select>
You can also spread into a (static) array before removing elements::
for (const option of [...document.querySelector('#form-select').options]) {
option.remove();
}
<select id="form-select">
<option>111</option>
<option>222</option>
<option>333</option>
</select>
Another option which just happens to work because the collection is live (but probably shouldn't be used, since it's not intuitive):
const { options } = document.querySelector('#form-select');
while (options.length) {
options[0].remove();
}
<select id="form-select">
<option>111</option>
<option>222</option>
<option>333</option>
</select>
You're removing items from an array, as you iterate through the array. So you have:
["one","two","three"]
then you remove the item at index 0, which is "one," leaving you with:
["two","three"]
next you remove the item at index 1, which is "three," leaving you with:
["two"]
there is no item at index 2, so the loop stops.
Iterate through the array in reverse instead:
const t = document.querySelector("#form-select")
for (let i = t.options.length-1; i >= 0; i--) {
t.removeChild(t.options[i])
}
<select id="form-select">
<option>111</option>
<option>222</option>
<option>333</option>
</select>
I see that your main goal is to understand the process that causes this to happen, so this should illustrate the problem for you:
var arr = ["one", "two", "three", "four", "five", "six"];
for(var i = 0; i < arr.length; i++){
console.log("i is " + i + ", so we are removing \"" + arr[i] + "\" from " + JSON.stringify(arr) + ".");
arr.splice(i, 1);
console.log("After that removal, the array is " + JSON.stringify(arr) + ". We'll now iterate i to " + (i + 1) + " and continue the loop.");
}
console.log("i is too high to grab a value from the array, so we're finished. We're left with " + JSON.stringify(arr) + ".");
This loop goes through the exact same type of process that your "for .. of" loop goes through to leave you with extras in the final result. The problem is that it is destroying its own indices as it's iterating through them, thus changing the value that i
is really referring to. When I'm facing this issue, I like to loop through the array backwards so I'm unaffected by my own destruction, like so:
var arr = ["one", "two", "three", "four", "five", "six"];
for(var i = arr.length - 1; i >= 0; i--){
console.log("i is " + i + ", so we are removing \"" + arr[i] + "\" from " + JSON.stringify(arr) + ".");
arr.splice(i, 1);
console.log("After that removal, the array is " + JSON.stringify(arr) + ". We'll now iterate i to " + (i - 1) + " and continue the loop.");
}
console.log("i is too low to grab a value from the array, so we're finished. We're left with " + JSON.stringify(arr) + ".");
I hope this helps you understand what's going on here thoroughly. If you have any questions, feel free to leave me a comment.
You are looping through the same array where index changes once you remove the item from array. Below is the sample where you can loop through options without index and remove it from array.
var selectOptions = document.querySelectorAll('#remove-option>option');
selectOptions.forEach(function(selectOption) {
selectOption.remove();
selectOption = null;
});
Here is the fiddle