DOM elementlarni tahrirlash funksiyasining JavaScriptda takrorlanish xatosi
Faraz qilaylik, bizda ma'lum bir ro'yxat mavjud:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Ro'yxatning o'zi va uning bandlarini alohida o'zgaruvchilarga olaylik:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Ro'yxat bandlarini paydo bo'ladigan input orqali tahrirlash imkoniyatini yarataylik:
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);
});
}
Endi faraz qilaylik, ro'yxatga yangi bandlar qo'shish imkoniyati bo'lishini xohlaymiz. Buning uchun ro'yxat ostida mos input bo'lsin:
<input id="adder">
Ushbu inputga havolani o'zgaruvchiga olaylik:
let adder = document.querySelector('#adder');
Input fokusni yo'qotganda, inputdan olingan matn bilan ro'yxatga yangi band qo'shilishi uchun qilaylik:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Endi faraz qilaylik, yangi qo'shilgan bandlar ham tahrirlanishi mumkin bo'lishini xohlaymiz. Ammo ular o'z-o'zidan tahrirlanmaydi, chunki biz ro'yxat bandlariga click hodisasi qayta ishlovchisini biriktirganimizda, bu bandlar hali mavjud emas edi.
Keling, ushbu muammoni hal qilishning turli usullarini ko'rib chiqaylik.
Birinchi yechim
Eng oddiy yechim - func funksiyasi kodini
takrorlash, uni yangi yaratilgan bandlar uchun ham bog'lash:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function func() {
// bu yerda biz kodni takrorlaymiz
});
ul.append(li);
});
Albatta, ushbu yechimda biz darhol kamchilikni ko'ramiz - kodni takrorlash noto'g'ri.
Ikkinchi yechim
Takrorlanish muammosini hal qilish uchun
func funksiyasini tashqariga chiqarib,
uni Function Declaration qilish mantiqan to'g'ri:
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);
}
Bu yerda bizni muammo kutmoqda.
Gap shundaki, bizning funksiyamiz tashqi ko'rinish
sohasidan olingan li o'zgaruvchisidan foydalangan.
Lekin funksiyani chiqarib olgandan so'ng, bu
o'zgaruvchi endi ko'rinmaydi!
Muammoni hal qilish uchun li ni
parametr sifatida uzatamiz:
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);
}
Va bu yerda bizning yechimimiz yana bir muammoni keltirib chiqaradi. Gap shundaki, hodisa qayta ishlovchisiga parametrni oddiygina uzatib bo'lmaydi:
for (let li of lis) {
li.addEventListener('click', func(li)); // ishlamaydi!
}
Ushbu muammoni hal qilish uchun funksiyani anonim qayta ishlovchi ichida chaqiramiz:
for (let li of lis) {
li.addEventListener('click', function() {
func(li);
});
}
Xuddi shunday yangi ro'yxat bandi yaratilganda ham qilamiz:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function() {
func(li);
});
ul.append(li);
});
Uchinchi yechim
Yanada nafisroq yechim mavjud. Biz oddiygina delegatsiyadan foydalanishimiz mumkin. Bu holda yangi ro'yxat bandlari bilan bog'liq muammo paydo bo'lmaydi:
ul.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') { // aynan li-ga bosilganini ushlaymiz, inputga emas
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;
});
}
});
Bu holda ro'yxat bandlari bo'yicha tsikl umuman kerak bo'lmaydi va yangi ro'yxat bandi yaratish kodi quyidagicha qisqaradi:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});