Klaida dėl DOM elementų redagavimo funkcijos dubliravimo JavaScript
Tarkime, kad turime tam tikrą sąrašą:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Gaukime patį sąrašą ir jo elementus į atskirus kintamuosius:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Padarykime taip, kad mūsų sąrašo elementus būtų galima redaguoti atsirandančiu įvesties lauku:
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);
});
}
Tarkime, kad dabar norime, kad į sąrašą galėtume pridėti naujų elementų. Tegul tam po sąrašu yra atitinkamas įvesties laukas:
<input id="adder">
Gaukime nuorodą į šį įvesties lauką į kintamąjį:
let adder = document.querySelector('#adder');
Padarykime taip, kad praradus fokusą įvesties lauke, į sąrašą būtų pridėtas naujas elementas su tekstu, paimtu iš mūsų įvesties lauko:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Tarkime, kad dabar norime, kad ir naujai pridėti elementai taip pat būtų redaguojami. Pats savaime jiems redagavimas neveiks, nes kai mes užkrovėme paspaudimo apdorotoją sąrašo elementams, šių elementų dar nebuvo.
Pažiūrėkime į galimus šios problemos sprendimo variantus.
Pirmasis sprendimas
Paprasčiausias sprendimas – pakartoti
funkcijos func kodą,
pririšant ją ir naujai sukurtiems elementams:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function func() {
// čia mes kartojame kodą
});
ul.append(li);
});
Žinoma, šiame sprendime iš karto matome trūkumą – kartoji kodą yra neteisinga.
Antrasis sprendimas
Norint išspręsti dubliravimo problemą,
logiška išimti funkciją func
į išorę, paversdami ją 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);
}
Čia mūsų ir laukia problema.
Faktas yra tas, kad mūsų funkcija naudojo
kintamąjį li, gautą
iš išorinės apimties.
Bet po funkcijos išėmimo šis
kintamasis dabar nematomas!
Norint išspręsti problemą, perduosime
mūsų li parametru:
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);
}
Ir čia mūsų sprendimas sukuria dar vieną problemą. Faktas yra tas, kad negalima tiesiog perduoti parametro į įvykio apdorotoją:
for (let li of lis) {
li.addEventListener('click', func(li)); // neveikia!
}
Norint išspręsti šią problemą, tiesiog iškviečiame mūsų funkciją anoniminio apdorotojo viduje:
for (let li of lis) {
li.addEventListener('click', function() {
func(li);
});
}
Ir analogiškai elgsimės kurdami naują sąrašo elementą:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function() {
func(li);
});
ul.append(li);
});
Trečiasis sprendimas
Egzistuoja elegantiškesnis sprendimas. Galima tiesiog pasinaudoti delegavimu. Šiuo atveju problema su naujais sąrašo elementais tiesiog nekils:
ul.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') { // fiksuojame būtent paspaudimą ant li, ne ant įvesties lauko
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;
});
}
});
Šiuo atveju ciklas per sąrašo elementus mums apskritai ir nebereikės, o kodas naujam sąrašo elementui sukurti susitrauks iki tokio:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});