17 of 17 menu

Error de duplicación de función para editar elementos DOM en JavaScript

Supongamos que tenemos una lista:

<ul> <li>1</li> <li>2</li> <li>3</li> </ul>

Obtengamos la lista misma y sus elementos en variables separadas:

let ul = document.querySelector('ul'); let lis = document.querySelectorAll('li');

Hagamos que los elementos de nuestra lista se puedan editar con un input que aparezca:

for (let li of lis) { li.addEventListener('click', function func() { let input = document.createElement('input'); input.value = list.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; li.addEventListener('click', func); }); li.removeEventListener('click', func); }); }

Supongamos ahora que queremos poder agregar nuevos elementos a la lista. Para ello, debajo de la lista tendremos un input correspondiente:

<input id="adder">

Obtenemos una referencia a este input en una variable:

let adder = document.querySelector('#adder');

Hagamos que al perder el foco del input se agregue un nuevo elemento a la lista con el texto tomado de nuestro input:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; ul.append(li); });

Supongamos ahora que queremos que los elementos recién agregados también se puedan editar. Por sí mismo, la edición no funcionará para ellos, porque cuando estábamos asignando el controlador de clics a los elementos de la lista, estos elementos aún no existían.

Veamos las posibles variantes para resolver este problema.

Solución primera

La solución más simple es duplicar el código de la función func, asignándola también para los elementos recién creados:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; li.addEventListener('click', function func() { // aquí duplicamos el código }); ul.append(li); });

Por supuesto, en esta solución vemos inmediatamente una desventaja: duplicar código no es correcto.

Solución segunda

Para resolver el problema de la duplicación, es lógico sacar la función func afuera, convirtiéndola en una Function Declaration:

function func() { let input = document.createElement('input'); input.value = list.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; li.addEventListener('click', func); }); li.removeEventListener('click', func); }

Aquí es donde nos espera el problema. El asunto es que nuestra función usaba la variable li, obtenida desde el ámbito externo. ¡Pero después de sacar la función, esta variable ya no es visible!

Para resolver el problema, pasaremos nuestra li como parámetro:

function func(li) { let input = document.createElement('input'); input.value = list.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; li.addEventListener('click', func); }); li.removeEventListener('click', func); }

Y aquí nuestra solución genera otro problema. El asunto es que no se puede simplemente pasar un parámetro a un controlador de eventos:

for (let li in lis) { li.addEventListener('click', func(li)); // ¡no funciona! }

Para resolver este problema, simplemente llamemos nuestra función dentro de un controlador anónimo:

for (let li of lis) { li.addEventListener('click', function() { func(li); }); }

Y procedamos de manera similar al crear un nuevo elemento de la lista:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; li.addEventListener('click', function() { func(li); }); ul.append(li); });

Solución tercera

Existe una solución más elegante. Simplemente se puede utilizar la delegación de eventos. En este caso, el problema con los nuevos elementos de la lista simplemente no surgirá:

ul.addEventListener('click', function(event) { if (event.target.tagName === 'LI') { // capturamos precisamente el clic en li, no en el input let li = event.target; let input = document.createElement('input'); input.value = li.textContent; li.textContent = ''; li.append(input); input.addEventListener('blur', function() { li.textContent = this.value; }); } });

En este caso, el ciclo por los elementos de la lista no nos hará falta en absoluto, y el código para crear un nuevo elemento de la lista se reducirá a esto:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; ul.append(li); });
Español
AfrikaansAzərbaycanБългарскиবাংলাБеларускаяČeštinaDanskDeutschΕλληνικάEnglishEestiSuomiFrançaisहिन्दीMagyarՀայերենIndonesiaItaliano日本語ქართულიҚазақ한국어КыргызчаLietuviųLatviešuМакедонскиMelayuမြန်မာNederlandsNorskPolskiPortuguêsRomânăРусскийසිංහලSlovenčinaSlovenščinaShqipСрпскиSrpskiSvenskaKiswahiliТоҷикӣไทยTürkmenTürkçeЎзбекOʻzbekTiếng Việt
Usamos cookies para el funcionamiento del sitio, análisis y personalización. El procesamiento de datos se realiza de acuerdo con la Política de privacidad.
aceptar todas configurar rechazar