Removendo opções da tag Select no Vanilla JavaScript com o loop "for .. of"

10

Ao tentar remover as opções do select, sempre resta uma, por quê?

<select id="form-select">   
<option>111</option>
<option>222</option>
<option>333</option>
</select>

Este JS não está funcionando:

var t = document.querySelector('#form-select'); 
for(var i of t.options) {
      t.remove(i.index)
    }

E isso não está funcionando também:

for(var i of document.querySelector('#form-select').options) {
  i.remove()
}

Sei que existem outras soluções para conseguir isso, mas gostaria de entender por que não está funcionando como deveria.

Piotr
fonte

Respostas:

7

A .optionscoleção é (infelizmente) ativa , portanto, a iteração dos itens da coleção ao vivo um por um e .removecada um resultará na manutenção de todos os itens ímpares. (Por exemplo, certo quando você remove o primeiro item, o [0]item de th da coleção se tornará imediatamente o próximo item na coleção - que costumava ser [1]passará a ser [0](e, em seguida, uma vez que você vá para o próximo índice no [1], o novo item em posição 0 não será repetido)

Use em document.querySelectorAllvez disso, que retorna uma coleção que é estática:

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>

Você também pode se espalhar para uma matriz (estática) antes de remover os elementos:

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>

Outra opção, que só acontece ao trabalho porque a coleção é ao vivo (mas provavelmente não deve ser utilizado, uma vez que não é intuitivo):

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>

CertainPerformance
fonte
3

Você está removendo itens de uma matriz à medida que itera pela matriz. Então você tem:

["one","two","three"]

você remove o item no índice 0, que é "um", deixando você com:

["two","three"]

Em seguida, você remove o item no índice 1, que é "três", deixando você com:

["two"]

não há nenhum item no índice 2, portanto, o loop é interrompido.

Itere através da matriz no sentido inverso :

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>

ligação simbólica
fonte
O não.options é um array, que é a fonte do problema - é um HTMLCollection, que está ativo. Se fosse uma matriz, seria estática e não haveria problemas.
CertainPerformance
11
@CertainPerformance remover elementos de uma matriz como você iterar através dele faz causa do problema que eu ilustrado.
symlink
11
@CertainPerformance Um HTMLOptionsCollectionobjeto atua nesse contexto como uma matriz.
symlink
2

Vejo que seu principal objetivo é entender o processo que faz com que isso aconteça, portanto, isso deve ilustrar o problema para você:

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) + ".");

Esse loop passa exatamente pelo mesmo tipo de processo que o loop "for .. of" passa para deixar extras no resultado final. O problema é que ele está destruindo seus próprios índices enquanto itera através deles, alterando o valor que irealmente está se referindo. Quando estou enfrentando esse problema, gosto de fazer um loop pela matriz para trás, para não ser afetado por minha própria destruição, da seguinte maneira:

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) + ".");

Espero que isso ajude você a entender bem o que está acontecendo aqui. Se você tiver alguma dúvida, não hesite em deixar um comentário.

Aaron Plocharczyk
fonte
0

Você está percorrendo a mesma matriz em que o índice é alterado depois que você remove o item da matriz. Abaixo está o exemplo em que você pode percorrer as opções sem índice e removê-lo da matriz.

var selectOptions = document.querySelectorAll('#remove-option>option');
selectOptions.forEach(function(selectOption) {
  selectOption.remove();
  selectOption = null;
});

Aqui está o violino

kichus14
fonte