Kļūda ar DOM elementu rediģēšanas funkcijas dublēšanu JavaScript
Pieņemsim, ka mums ir noteikts saraksts:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Iegūstam pašu sarakstu un tā punktus atsevišķos mainīgos:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Uztaisīsim tā, lai mūsu saraksta punktus varētu rediģēt ar parādāmu ievades lauku:
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);
});
}
Pieņemsim, ka tagad mēs vēlamies, lai sarakstā varētu pievienot jaunus punktus. Lai to izdarītu, zem saraksta mums būs atbilstošs ievades lauks:
<input id="adder">
Iegūstam atsauci uz šo ievades lauku mainīgajā:
let adder = document.querySelector('#adder');
Uztaisīsim tā, lai zaudējot fokusu no ievades lauka, sarakstam pievienotos jauns punkts ar tekstu, kas ņemts no mūsu ievades lauka:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Pieņemsim, ka tagad mēs vēlamies, lai arī jaunpievienotie punkti tiktu rediģējami. Paši par sevi rediģēšana tiem nestrādās, jo, kad mēs pievienojām klikšķa notikumu apstrādi saraksta punktiem, šo punktu vēl nebija.
Apskatīsimies iespējamos šīs problēmas risinājuma variantus.
Pirmais risinājums
Vienkāršākais risinājums ir dubultot
funkcijas func kodu, piesiet to
arī jaunizveidotajiem punktiem:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function func() {
// šeit mēs dubultojam kodu
});
ul.append(li);
});
Protams, šajā risinājumā mēs uzreiz redzam trūkumu - koda dublēšana nav pareiza.
Otrais risinājums
Lai atrisinātu dublēšanas problēmu,
loģiski ir iznest funkciju func
ārā, padarot to par 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);
}
Šeit mūs sagaida problēma.
Lieta ir tāda, ka mūsu funkcija izmantoja
mainīgo li, kas iegūts
no ārējās redzamības apgabala.
Bet pēc funkcijas iznešanas ārā šis
mainīgais vairs nav redzams!
Lai atrisinātu problēmu, mēs padodam
mūsu li kā parametru:
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);
}
Un šeit mūsu risinājums rada vēl vienu problēmu. Lieta ir tāda, ka nevar vienkārši tā padot parametru notikuma apstrādes funkcijai:
for (let li in lis) {
li.addEventListener('click', func(li)); // nestrādā!
}
Lai atrisinātu šo problēmu, vienkārši izsaucam mūsu funkciju anonīmas apstrādes funkcijas iekšienē:
for (let li of lis) {
li.addEventListener('click', function() {
func(li);
});
}
Un līdzīgi rīkojamies veidojot jaunu saraksta punktu:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function() {
func(li);
});
ul.append(li);
});
Trešais risinājums
Pastāv elegantāks risinājums. Var vienkārši izmantot deleģēšanu. Šajā gadījumā problēma ar jaunajiem saraksta punktiem vienkārši neradīsies:
ul.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') { // ķeram tieši klikšķi uz li, ne uz ievades lauku
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;
});
}
});
Šajā gadījumā cikls pa saraksta punktiem mums vispār nebūs vajadzīgs, un kods jauna saraksta punkta izveidošanai saīsināsies līdz šim:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});