JavaScript да DOM элементларини таҳрирлаш функциясини такрорлаш хатолики
Фарз қилайлик, бизда маълум рўйхат бор:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Рўйхатни ва унинг пунктларини алохида ўзгарувчиларга оламиз:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Рўйхатимизнинг пунктларини пайдо бўладиган инпут орқали таҳрирлаш имконини яратамиз:
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);
});
}
Энди фарз қилайлик, биз рўйхатга янги пунктлар қўшишни истаймиз. Бунга рўйхат ostida маълум инпут бор деб фарз қилайлик:
<input id="adder">
Ушбу инпутга ҳаволани ўзгарувчига оламиз:
let adder = document.querySelector('#adder');
Инпутда фокус йўқотганда рўйхатга ушбу инпутдан олинган матн билан янги пункт қўшиладиган қилиб яратамиз:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Энди фарз қилайлик, биз янги қўшилган пунктларни ҳам таҳрирлаш имкони бўлишини истаймиз. Улар учун таҳрирлаш ўзи-ўзидан ишламейди, чунки биз рўйхат пунктларига клик ҳандлерини боглаганда, бу пунктлар ҳали мавжуд эмас эди.
Келинг, бу муаммони ҳал қилишнинг турли вариантларини кўриб чиқайлик.
Биринчи ҳал этиш
Энг содда ҳал - бу func функцияси кодini
такрорлаш, уни янги яратилган пунктлар учун ҳам боглаш:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function func() {
// бу ерда биз кодни такрорлаймиз
});
ul.append(li);
});
Албатта, бу ҳал этишда биз дарҳол камчиликни кўрамиз - кодни такрорлаш тўғри эмас.
Иккинчи ҳал этиш
Такрорланиш муаммосini ҳал қилиш учун
func функциясини 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);
}
Ана ўша ерда бизни муаммо кутмоқда.
Гапи шундаки, бизнинг функциямиз
ташқи кўриш доирасидан олинган li
ўзгарувчисидан фойдаланган.
Аммо функцияни чиқарганимиздан кейин
енди бу ўзгарувчи кўринмайди!
Муаммони ҳал қилиш учун бизнинг
li ўзгарувчимизни параметр сифатида ўтказамиз:
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);
}
Ва ўша ерда бизнинг ҳал этишимиз яна бир муаммони келтириб чиқаради. Гапи шундаки, ҳодиса ҳандлерига параметрни оддий ҳолда ўтказиб бўлмайди:
for (let li in lis) {
li.addEventListener('click', func(li)); // ишламайди!
}
Бу муаммони ҳал қилиш учун бизнинг функциямизни номсиз ҳандлер ичида чақирамиз:
for (let li of lis) {
li.addEventListener('click', function() {
func(li);
});
}
Ва шуга ўхшаш янги рўйхат пунктини яратишда ҳам амал қиламиз:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function() {
func(li);
});
ul.append(li);
});
Учинчи ҳал этиш
Янада зебу-зийнатли ҳал мавжуд. Биз жуда оддийгина делегированиедан фойдаланишимиз мумкин. Бу ҳолда янги рўйхат пунктлари билан боглик муаммо умуман пайдо бўлмайди:
ul.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') { // инпут эмас, балки li даги кликни тутамиз
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;
});
}
});
Бу ҳолда рўйхат пунктлари буйича цикл умуман керак бўлмайди, ва янги рўйхат пунктини яратиш коди шундай қисқариши мумкин:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});