A DOM elemek szerkesztésére szolgáló függvény ismétlődő hibája JavaScriptben
Tegyük fel, hogy van egy listánk:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Kérjük le magát a listát és annak elemeit külön változókba:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Tegyük lehetővé, hogy a listánk elemeit egy megjelenő input mezővel lehessen szerkeszteni:
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);
});
}
Tegyük fel most, hogy azt szeretnénk, hogy a listába lehessen új elemeket hozzáadni. Tegyük fel, hogy ehhez a lista alatt lesz egy megfelelő input mező:
<input id="adder">
Kérjük le ezt az input mezőt egy változóba:
let adder = document.querySelector('#adder');
Tegyük lehetővé, hogy amikor az input mező elveszti a fókuszt, akkor a listához adjon hozzá egy új elemet a szöveggel, amely az input mezőből származik:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Tegyük fel most, hogy azt szeretnénk, hogy az újonnan hozzáadott elemeket is lehessen szerkeszteni. Önmagában a szerkesztés nem fog működni rájuk, mert amikor a kattintás eseménykezelőt rendeltük a listaelemekhez, ezek az elemek még nem is léteztek.
Nézzük meg a lehetséges megoldási lehetőségeket erre a problémára.
Első megoldás
A legegyszerűbb megoldás - hogy megkettézzük
a func függvény kódját,
összekötve azt
az újonnan létrehozott elemekkel is:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function func() {
// itt megkettézzük a kódot
});
ul.append(li);
});
Természetesen ebben a megoldásban azonnal látjuk a hátrányt - a kód megkettőzése nem helyes.
Második megoldás
A megkettőzés problémájának megoldása érdekében
logikus a func függvényt
kívülre helyezni, Function Declaration-né téve:
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);
}
Itt vár ránk a probléma.
Az a helyzet, hogy a függvényünk használta
a li változót, amelyet
a külső hatókörből kaptunk.
De a függvény kívülre helyezése után ez a
változó már nem látható!
A probléma megoldása érdekében adjuk át
a li változót paraméterként:
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);
}
És itt a megoldásunk egy további problémát vet fel. Az a helyzet, hogy nem lehet egyszerűen paramétert átadni egy eseménykezelőnek:
for (let li of lis) {
li.addEventListener('click', func(li)); // nem működik!
}
A probléma megoldásához egyszerűen hívjuk meg a függvényünket egy névtelen eseménykezelőn belül:
for (let li of lis) {
li.addEventListener('click', function() {
func(li);
});
}
És hasonlóan járjunk el az új listaelem létrehozásakor:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function() {
func(li);
});
ul.append(li);
});
Harmadik megoldás
Létezik egy elegánsabb megoldás is. Egyszerűen használhatjuk a delegálást. Ebben az esetben az új listaelemekkel kapcsolatos probléma egyszerűen nem fog felmerülni:
ul.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') { // pontosan az li-re kattintást fogjuk el, nem az inputra
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;
});
}
});
Ebben az esetben a listaelemeken való ciklus egyáltalán nem is lesz szükséges, és az új listaelem létrehozásához szükséges kód ilyenre rövidül:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});