Eliminar opciones de la etiqueta Seleccionar en JavaScript de Vanilla con el bucle "for .. of"

10

Al intentar eliminar las opciones de select, siempre queda una, ¿por qué?

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

Este JS no funciona:

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

Y esto tampoco funciona:

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

Sé que hay otras soluciones para lograrlo, pero me gustaría entender por qué no funciona como se supone que debería

Piotr
fuente

Respuestas:

7

La .optionscolección es (desafortunadamente) en vivo , por lo que iterar sobre los elementos de la colección en vivo uno por uno y hacer que .removetodos se mantengan. (Por ejemplo, justo cuando se quita el primer elemento, el [0]artículo del th de la colección se convertirá inmediatamente en el siguiente elemento de la colección - lo que solía ser [1]se convertirá en [0](y luego una vez que vaya a la siguiente índice en [1]el nuevo elemento en la posición 0 no se repetirá)

Utilizar document.querySelectorAll lugar, que devuelve una colección que es 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>

También puede extenderse a una matriz (estática) antes de eliminar 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>

Otra opción que se acaba pasa a trabajar porque la colección es en vivo (pero probablemente no se debe utilizar, ya que no es intuitiva):

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>

Cierto rendimiento
fuente
3

Estás eliminando elementos de una matriz a medida que recorres la matriz. Así que tienes:

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

luego elimina el elemento en el índice 0, que es "uno", dejándolo con:

["two","three"]

a continuación, elimina el elemento en el índice 1, que es "tres", dejándolo con:

["two"]

no hay ningún elemento en el índice 2, por lo que el ciclo se detiene.

En su lugar, itere a través de la matriz en reversa :

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>

enlace simbólico
fuente
El .optionses no una matriz, que es la fuente del problema - más bien, se trata de una HTMLCollection, que es en vivo. Si fuera una matriz, sería estática y no habría ningún problema.
CertainPerformance
1
@CertainPerformance eliminación de elementos de un array como iterar a través de él hace causa el problema he ilustrado.
enlace simbólico
1
@CertainPerformance Un HTMLOptionsCollectionobjeto actúa en este contexto como una matriz.
enlace simbólico
2

Veo que su objetivo principal es comprender el proceso que hace que esto suceda, por lo que esto debería ilustrar el problema para usted:

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

Este ciclo pasa exactamente por el mismo tipo de proceso por el que pasa tu ciclo "for .. of" para dejarte con extras en el resultado final. El problema es que está destruyendo sus propios índices a medida que los itera, cambiando así el valor al que irealmente se refiere. Cuando me enfrento a este problema, me gusta recorrer la matriz hacia atrás para que mi propia destrucción no me afecte, así:

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 esto te ayude a comprender lo que está sucediendo aquí a fondo. Si tiene alguna pregunta, no dude en dejarme un comentario.

Aaron Plocharczyk
fuente
0

Está recorriendo la misma matriz donde el índice cambia una vez que elimina el elemento de la matriz. A continuación se muestra el ejemplo donde puede recorrer las opciones sin índice y eliminarlo de la matriz.

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

Aquí está el violín

kichus14
fuente