DOM элементтерін өңдеу функциясын қайталау қатесі JavaScript-те
Бізде келесі тізім бар делік:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Тізімнің өзін және оның элементтерін бөлек айнымалыларға алайық:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Тізім элементтерін пайда болған input арқылы өңдеуге болатын етейік:
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);
});
}
Енді тізімге жаңа элементтер қосуға болатын болайық. Ол үшін тізімнің астында сәйкес input болсын:
<input id="adder">
Осы input-қа сілтемені айнымалыға алайық:
let adder = document.querySelector('#adder');
Input-та фокус жоғалған кезде, input-тан алынған мәтінмен тізімге жаңа элемент қосылатын етейік:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Енді жаңа қосылған элементтерді де өңдеуге болатын болғанын қалаймыз. Бірақ олар үшін өңдеу өздігінен жұмыс істемейді, себебі біз тізім элементтеріне клик өңдегішін қосқан кезде, бұл элементтер әлі жоқ еді.
Осы мәселені шешудің мүмкін нұсқаларын қарастырайық.
Бірінші шешім
Ең қарапайым шешім - func функциясының
кодын қайталап, оны жаңа құрылған элементтер
үшін де қосу:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function func() {
// мұнда біз кодты қайталаймыз
});
ul.append(li);
});
Әрине, бұл шешімде біз бірден кемшілікті көреміз - кодты қайталау дұрыс емес.
Екінші шешім
Қайталау мәселесін шешу үшін func
функциясын сыртқа шығарып, оны 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);
}
Дәл осы жерде бізді мәселе күтіп тұр.
Мәселе мынада, біздің функция сыртқы
көрінетін аймақтан алынған li
айнымалысын пайдаланды. Бірақ функцияны
шығарғаннан кейін бұл айнымалы енді көрінбейді!
Мәселені шешу үшін біздің li-ді
параметр ретінде берейік:
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);
}
Осы жерде біздің шешіміміз тағы бір мәселе туғызады. Мәселе мынада, оқиға өңдегішіне параметрді жай ғана беруге болмайды:
for (let li of 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-ға басылған кликті ұстаймыз, input-қа емес
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);
});