17 of 17 menu

Błąd duplikacji funkcji edycji elementów DOM w JavaScript

Załóżmy, że mamy pewną listę:

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

Pobierzmy samą listę i jej elementy do oddzielnych zmiennych:

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

Sprawmy, aby elementy naszej listy można było edytować pojawiającym się inputem:

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); }); }

Załóżmy teraz, że chcemy, aby do listy można było dodawać nowe elementy. Załóżmy, że w tym celu pod listą będzie odpowiedni input:

<input id="adder">

Pobierzmy referencję do tego inputa do zmiennej:

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

Sprawmy, aby po utracie fokusu inputa do listy dodał się nowy element z tekstem pobranym z naszego inputa:

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

Załóżmy teraz, że chcemy, aby również nowo dodane elementy również można było edytować. Samo z siebie edytowanie dla nich nie zadziała, ponieważ kiedy wieszaliśmy obsługę kliknięcia na elementy listy, tych elementów jeszcze nie było.

Przyjrzyjmy się możliwym wariantom rozwiązania tego problemu.

Rozwiązanie pierwsze

Najprostsze rozwiązanie - to zduplikować kod funkcji func, przywiązując ją również dla nowo utworzonych elementów:

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

Oczywiście, w tym rozwiązaniu od razu widzimy wadę - duplikowanie kodu jest nieprawidłowe.

Rozwiązanie drugie

Aby rozwiązać problem duplikowania logicznie wynieść funkcję func na zewnątrz, tworząc ją jako 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); }

Tutaj właśnie czai się na nas problem. Chodzi o to, że nasza funkcja używała zmiennej li, uzyskanej z zewnętrznego zakresu widoczności. Ale po wyniesieniu funkcji ta zmienna teraz nie jest widoczna!

Aby rozwiązać problem, będziemy przekazywać naszą li parametrem:

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); }

I tutaj nasze rozwiązanie generuje jeszcze jeden problem. Chodzi o to, że nie można po prostu tak przekazać parametru do obsługi zdarzenia:

for (let li of lis) { li.addEventListener('click', func(li)); // nie działa! }

Aby rozwiązać ten problem, po prostu wywołajmy naszą funkcję wewnątrz anonimowej obsługi:

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

I podobnie postąpmy przy tworzeniu nowego elementu listy:

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

Rozwiązanie trzecie

Istnieje bardziej eleganckie rozwiązanie. Można po prostu skorzystać z delegowania zdarzeń. W tym przypadku problem z nowymi elementami listy po prostu nie powstanie:

ul.addEventListener('click', function(event) { if (event.target.tagName === 'LI') { // łapiemy właśnie kliknięcie na li, nie na 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; }); } });

W tym przypadku pętla po elementach listy w ogóle nie będzie nam potrzebna, a kod dla tworzenia nowego elementu listy skróci się do takiego:

adder.addEventListener('blur', function() { let li = document.createElement('li'); li.textContent = this.value; ul.append(li); });
Polski
AfrikaansAzərbaycanБългарскиবাংলাБеларускаяČeštinaDanskDeutschΕλληνικάEnglishEspañolEestiSuomiFrançaisहिन्दीMagyarՀայերենIndonesiaItaliano日本語ქართულიҚазақ한국어КыргызчаLietuviųLatviešuМакедонскиMelayuမြန်မာNederlandsNorskPortuguêsRomânăРусскийසිංහලSlovenčinaSlovenščinaShqipСрпскиSrpskiSvenskaKiswahiliТоҷикӣไทยTürkmenTürkçeЎзбекOʻzbekTiếng Việt
Wykorzystujemy pliki cookie do działania strony, analizy i personalizacji. Przetwarzanie danych odbywa się zgodnie z Polityką prywatności.
zaakceptuj wszystkie dostosuj odrzuć