Ralat Penduaan Fungsi Pengeditan Unsur DOM dalam JavaScript
Katakan kita mempunyai senarai tertentu:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Dapatkan senarai itu sendiri dan itemnya ke dalam pembolehubah berasingan:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('li');
Mari kita buat supaya item senarai kita boleh diedit menggunakan 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 katakan kita mahu menambah item baharu ke dalam senarai. Katakan untuk ini, di bawah senarai kita mempunyai input yang sepadan:
<input id="adder">
Dapatkan pautan ke input ini ke dalam pembolehubah:
let adder = document.querySelector('#adder');
Mari kita buat supaya apabila input kehilangan fokus, item baharu dengan teks yang diambil dari input kami ditambahkan ke senarai:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});
Sekarang katakan kita mahu item yang baru ditambah juga boleh diedit. Dengan sendirinya, pengeditan tidak akan berfungsi untuk mereka, kerana ketika kami melampirkan pengendali klik pada item senarai, item tersebut belum wujud.
Mari kita lihat pilihan penyelesaian yang mungkin untuk masalah ini.
Penyelesaian Pertama
Penyelesaian paling mudah - ialah menduakan
kod fungsi func, dengan melampirkannya
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 menduakan kod
});
ul.append(li);
});
Sudah tentu, dalam penyelesaian ini kita langsung nampak kelemahannya - menduakan kod adalah tidak betul.
Penyelesaian Kedua
Untuk menyelesaikan masalah penduaan,
adalah logik untuk mengeluarkan fungsi func
ke luar, 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 ialah fungsi kami menggunakan
pembolehubah li, yang diperoleh
dari skop luaran.
Tetapi selepas fungsi dikeluarkan,
pembolehubah itu sekarang tidak kelihatan!
Untuk menyelesaikan masalah, kami akan menghantar
li kami 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', func);
});
li.removeEventListener('click', func);
}
Dan di sini penyelesaian kami menimbulkan satu lagi masalah. Masalahnya ialah kita tidak boleh begitu sahaja menghantar parameter kepada pengendali peristiwa:
for (let li of lis) {
li.addEventListener('click', func(li)); // tidak berfungsi!
}
Untuk menyelesaikan masalah ini, panggil sahaja fungsi kami di dalam pengendali tanpa nama:
for (let li of lis) {
li.addEventListener('click', function() {
func(li);
});
}
Dan lakukan perkara yang sama apabila mencipta item senarai baharu:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
li.addEventListener('click', function() {
func(li);
});
ul.append(li);
});
Penyelesaian Ketiga
Terdapat penyelesaian yang lebih elegan. Anda boleh menggunakan delegasi acara. Dalam kes ini masalah dengan item senarai baharu sahaja tidak akan timbul:
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 kes ini, gelung melalui item senarai langsung tidak diperlukan oleh kami, dan kod untuk mencipta item senarai baharu akan dipendekkan kepada ini:
adder.addEventListener('blur', function() {
let li = document.createElement('li');
li.textContent = this.value;
ul.append(li);
});