Bagaimana saya bisa mendapatkan acara klik di dalam innerHTML?

12

Saya memiliki tata letak ini yang dibuat secara dinamis:

for (let i = 1; i < 10; i++) {
  document.querySelector('.card-body').innerHTML += `<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title` + i + `">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `
  document.getElementById("title" + i).addEventListener('click', function() {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
  <!-- person -->
</div>

Dan saya ingin mendapatkan acara klik pada masing h4 class="name"- masing dan menunjukkan log dengan nomor yang iterkait.

Namun, console.loghanya menunjukkan yang iterkait terakhir ( i=9dalam hal ini), dan tidak berfungsi dengan iangka lainnya . Mengapa ini terjadi? Apa yang harus saya lakukan?

Luiz Adorno
sumber
3
Mungkin menggunakan Document.createElement () daripada melewatkan html-string & Anda akan memiliki waktu yang jauh lebih mudah & saya pikir Anda akan berakhir dengan kode yang lebih baik pada akhirnya.
admcfajn
1
Anda harus mengulang-ulang node Anda dan melampirkan acara yang tidak dapat Anda lampirkan ke string
Eugen Sunic
Pertanyaan yang bagus, itu agak sulit untuk dipastikan, saya pikir mungkin untuk melakukan rendering posting acara .. Misalnya setelah forloop Anda memanggil metode untuk menerapkan peristiwa ke elemen-elemen itu, tapi saya tidak tahu. akan jsFiddle untuk menguji.
Pogrindis
Dibuat pengendali acara yang didelegasikan untuk .card-bodydan periksa apakah elemen ( target) adalah jangkar dengan dan ID yang dimulai dengan title.
hungerstar
1
Ah, baru saja memposting jawaban. Memilih untuk membuka kembali. Penutupan tidak perlu, masalahnya adalah innerHTML += ...membunuh pendengar acara yang ditetapkan sebelumnya, bukan penutupan. Gunakan document.createElementsebagai gantinya. Lihat utas ini
ggorlen

Jawaban:

8

innerHTMLredraws html penuh, sebagai akibatnya semua acara yang terlampir sebelumnya hilang. MenggunakaninsertAdjacentHTML()

for(let i=1;i<10;i++){
    document.querySelector('.card-body').insertAdjacentHTML('beforeend',`<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title`+i+`">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `);
   document.getElementById("title"+i).addEventListener('click', function () {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
<!-- person -->
</div>

Mamun
sumber
3
Wow .. Saya sudah bermain-main dengan hal-hal simular dan tidak pernah sekali pun bertemu insertAdjacentHTML- Setiap hari schoolday, sangat bagus.
Pogrindis
1
Sangat keren dan +1, tetapi tampaknya masih mahal dan tidak perlu harus document.getElementByIdsegera digunakan setelah menambahkan HTML. Jika Anda menambahkan ratusan elemen, ini banyak pekerjaan traverse.
ggorlen
@ggorlen Saya agak setuju pada bidang perf yang menarik akan menggunakan kelas namedan menyesuaikan acara hanya kelas sambil melewati elemen (atau mungkin hanya membaca dari acara itu sendiri) lebih efisien?
Pogrindis
1
Tidak yakin. Tergantung pada penggunaan yang sebenarnya - saran saya bisa jadi optimasi prematur. Anda harus profil / benchmark dan lihat. Tetapi jika document.createElementdapat digunakan, saya pikir itu umumnya pendekatan dasar terbaik. Peningkatan lain yang mungkin untuk posting ini adalah menggunakan dua loop: satu untuk membangun semua HTML, lalu yang lain untuk mengambil semua elemen menggunakan kelas dalam satu document.querySelectorAllpanggilan dan menambahkan pendengar acara.
ggorlen
1
Pantas untuk dijadikan patokan, saya tidak bisa memikirkan hal lain yang lebih baik untuk dilakukan malam ini! :)
Pogrindis
8

Menggunakan +=pada innerHTMLpendengar acara Rusak pada unsur-unsur di dalam HTML. Cara yang benar untuk melakukan ini adalah menggunakan document.createElement. Berikut ini contoh minimal yang lengkap:

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);
  anchor.id = "title" + i;
  anchor.href= "#";
  anchor.textContent = "link " + i;
  document.getElementById("title" + i)
    .addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

Tentu saja, document.getElementByIdtidak perlu di sini karena kita baru saja membuat objek di blok loop. Ini menghemat jalan-jalan pohon yang berpotensi mahal.

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);    
  anchor.href= "#";
  anchor.textContent = "link " + i;
  anchor.addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

Jika Anda memiliki potongan acak HTML yang dirubah seperti dalam contoh Anda, Anda dapat menggunakan createElement("div"), mengatur satu innerHTMLkali ke chunk dan menambahkan pendengar sesuai kebutuhan. Kemudian tambahkan divs sebagai anak-anak elemen wadah Anda.

for (let i = 1; i < 10; i++) {
  const rawHTML = `<div><a href="#" id="link-${i}">link ${i}</a></div>`;
  const div = document.createElement("div");
  document.body.appendChild(div);
  div.href= "#";
  div.innerHTML = rawHTML;
  div.querySelector(`#link-${i}`)
    .addEventListener("click", e => console.log(i));
}

Ggorlen
sumber
1
Ini adalah pendekatan yang saya pertimbangkan, dan saya akan memberikan solusi ini, tetapi jawaban yang lain juga bagus.
Pogrindis
Terima kasih atas jawaban, tetapi saya mencoba membangun tata letak dengan createElement dan saya tidak berhasil
Luiz Adorno
1
Tidak masalah. Semoga Anda menyadari jika Anda memiliki banyak HTML, Anda dapat membuat wadah elemen root seperti <div>, lalu div.innerHTML += yourHugeChunkOfHTML. Jadi tidak ada alasan ini tidak bisa digunakan untuk kasus penggunaan apa pun.
ggorlen
1
Saya akan mencoba membangun tata letak ini dengan rekomendasi Anda, saya takut menggunakan "document.getElementById" beberapa kali dan memperlambat kinerja
Luiz Adorno