Kesalahan Duplikasi Fungsi Edit Elemen DOM di JavaScript
Misalkan kita memiliki sebuah daftar:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Mari dapatkan daftar itu sendiri dan item-itemnya ke dalam variabel terpisah:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Mari kita buat agar item-item dari daftar kita dapat diedit dengan input yang muncul:
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);
});
}
Sekarang misalkan kita ingin menambahkan item baru ke dalam daftar. Misalkan untuk ini, di bawah daftar kita ada input yang sesuai:
<input id="adder">
Dapatkan referensi ke input ini ke dalam variabel:
let adder = document.querySelector('#adder');
Mari kita buat agar ketika kehilangan fokus, input menambahkan item baru ke daftar dengan teks yang diambil dari input kita:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Sekarang misalkan kita ingin item yang baru ditambahkan juga dapat diedit. Secara sendiri, pengeditan tidak akan bekerja untuk mereka, karena ketika kita memasang penangan klik pada item daftar, item-item ini belum ada.
Mari kita lihat beberapa opsi yang memungkinkan untuk memecahkan masalah ini.
Solusi Pertama
Solusi paling sederhana adalah menduplikasi
kode fungsi func, dengan mengikatnya
juga untuk item yang baru dibuat:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function func() {
// di sini kita menduplikasi kode
});
ul.append(li);
});
Tentu saja, dalam solusi ini kita langsung melihat kelemahannya - menduplikasi kode itu tidak benar.
Solusi Kedua
Untuk menyelesaikan masalah duplikasi,
masuk akal untuk mengambil fungsi func
keluar, menjadikannya 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);
}
Di sinilah masalah menunggu kita.
Masalahnya adalah fungsi kita menggunakan
variabel li, yang didapatkan
dari ruang lingkup luar.
Tapi setelah fungsi dikeluarkan,
variabel itu sekarang tidak terlihat!
Untuk memecahkan masalah, kita akan meneruskan
li kita sebagai parameter:
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', function() {
func(li);
});
});
li.removeEventListener('click', function() {
func(li);
});
}
Dan di sini solusi kita menimbulkan masalah lain. Masalahnya adalah kita tidak bisa begitu saja meneruskan parameter ke penangan event:
for (let li of lis) {
li.addEventListener('click', func(li)); // tidak bekerja!
}
Untuk mengatasi masalah ini, cukup panggil fungsi kita di dalam penangan anonim:
for (let li of lis) {
li.addEventListener('click', function() {
func(li);
});
}
Dan lakukan hal yang sama saat membuat item daftar baru:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function() {
func(li);
});
ul.append(li);
});
Solusi Ketiga
Ada solusi yang lebih elegan. Kita bisa memanfaatkan delegasi event. Dalam hal ini masalah dengan item daftar baru tidak akan muncul:
ul.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') { // tangkap klik tepat pada li, bukan pada 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;
});
}
});
Dalam hal ini, loop melalui item daftar pada dasarnya tidak akan kita butuhkan, dan kode untuk membuat item daftar baru akan dipersingkat menjadi seperti ini:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});