17 of 17 menu

Fel med duplicering av funktion för redigering av DOM-element i JavaScript

Låt oss säga att vi har en lista:

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

Låt oss hämta själva listan och dess punkter i separata variabler:

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

Låt oss göra så att punkterna i vår lista kan redigeras med en input som visas:

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

Låt oss nu säga att vi vill kunna lägga till nya punkter i listan. Låt oss för detta ha en motsvarande input under listan:

<input id="adder">

Låt oss hämta en referens till denna input i en variabel:

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

Låt oss göra så att när input förlorar fokus läggs en ny punkt till i listan med texten tagen från vår input:

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

Låt oss nu säga att vi vill att även de nyligen tillagda punkterna ska kunna redigeras. Själv klart kommer redigering inte att fungera för dem automatiskt, för när vi lade på klick-hanteraren på listpunkterna fanns dessa punkter ännu inte.

Låt oss titta på möjliga lösningar på detta problem.

Första lösningen

Den enklaste lösningen är att duplicera koden för funktionen func, och binda den även för nyskapade punkter:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; li.addEventListener('click', function func() { // här duplicerar vi koden }); ul.append(li); });

Naturligtvis ser vi omedelbart en nackdel med denna lösning - det är felaktigt att duplicera kod.

Andra lösningen

För att lösa dupliceringsproblemet är det logiskt att flytta funktionen func ut, och göra den till en Function Declaration:

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

Här väntar problemet på oss. Saken är den att vår funktion använde variabeln li, som hämtades från det yttre scopet. Men efter att ha flyttat funktionen är denna variabel nu inte synlig!

För att lösa problemet låt oss skicka vår li som en parameter:

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

Och här skapar vår lösning ytterligare ett problem. Saken är den att man inte bara kan skicka en parameter till en händelsehanterare:

for (let li of lis) { li.addEventListener('click', func(li)); // fungerar inte! }

För att lösa detta problem, anropa helt enkelt vår funktion inuti en anonym hanterare:

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

Och låt oss göra på samma sätt när vi skapar en ny punkt i listan:

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

Tredje lösningen

Det finns en mer elegant lösning. Man kan helt enkelt använda sig av delegering. I detta fall uppstår inte problemet med nya punkter i listan överhuvudtaget:

ul.addEventListener('click', function(event) { if (event.target.tagName === 'LI') { // fångar specifikt klick på li, inte på 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; }); } });

I detta fall behöver vi inte ens en loop över listpunkterna, och koden för att skapa en ny punkt i listan förkortas till detta:

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