Come posso ottenere l'evento click all'interno di un innerHTML?


12

Ho questo layout che è stato creato in modo dinamico:

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>

E voglio ottenere l'evento fare clic su ciascuno h4 class="name"e mostrare un registro con il numero irelativo.

Tuttavia, console.logmostra solo gli ultimi icorrelati ( i=9in questo caso) e non funziona con gli altri inumeri. Perché succede? Cosa devo fare?


3
Forse usa Document.createElement () invece di passare html-string e avrai un tempo molto più semplice e penso che alla fine otterrai un codice migliore.
admcfajn,

1
dovrai ripetere il ciclo tra i tuoi nodi e collegare un evento che non puoi collegare in una stringa
Eugen Sunic

Bella domanda, sicuramente è un po 'complicato, penso che potrebbe essere possibile catturare il rendering di un post di evento .. Ad esempio dopo il tuo forciclo che chiama un metodo per applicare eventi a quegli elementi, ma non lo so. jsFiddle per testare.
Pogrindis,

Creato un gestore eventi delegato per .card-bodye controlla se l'elemento ( target) è un ancoraggio con e ID che inizia con title.
Hungerstar,

1
Ah, sto per pubblicare una risposta. Votato per riaprire. Non sono necessarie chiusure, il problema è innerHTML += ...uccidere i listener di eventi impostati in precedenza, non la chiusura. Usa document.createElementinvece. Vedi questa discussione
ggorlen

Risposte:


8

innerHTMLridisegna l'intero HTML, di conseguenza tutti gli eventi precedentemente collegati vengono persi. UsoinsertAdjacentHTML()

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>


3
Wow .. Sono stato in giro con cose similari e mai una volta mi sono imbattuto insertAdjacentHTML- tutti i giorni un giorno di scuola, davvero bello.
Pogrindis,

1
Molto interessante e +1, ma sembra comunque costoso e inutile doverlo usare document.getElementByIdsubito dopo aver aggiunto l'HTML. Se stai aggiungendo centinaia di elementi, questo è molto lavoro di traversata.
ggorlen,

@ggorlen Sono d'accordo sul fatto che l'area perf per interesse sarebbe usare la classe namee attirare l'evento solo su quella classe mentre passare attraverso l'elemento (o forse solo leggere dall'evento stesso) sarebbe più efficiente?
Pogrindis,

1
Non sono sicuro. Dipende dall'uso effettivo: il mio suggerimento potrebbe essere l'ottimizzazione prematura. Dovresti profilare / confrontare e vedere. Ma se document.createElementpuò essere usato, penso che sia generalmente il miglior approccio di base. Un altro possibile miglioramento a questo post è l'uso di due loop: uno per costruire tutto l'HTML, poi un altro per prendere tutti gli elementi usando una classe in una document.querySelectorAllchiamata e aggiungere i listener di eventi.
ggorlen,

1
Sicuramente vale un punto di riferimento, non riesco a pensare a qualcosa di meglio da fare stasera! :)
Pogrindis,

8

L'utilizzo di +=on innerHTMLdistrugge i listener di eventi sugli elementi all'interno dell'HTML. Il modo corretto per farlo è usare document.createElement. Ecco un esempio minimo e completo:

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;
}

Naturalmente, document.getElementByIdnon è necessario qui poiché abbiamo appena creato l'oggetto nel blocco loop. Ciò consente di risparmiare passeggiate sugli alberi potenzialmente costose.

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;
}

Se hai blocchi arbitrari di HTML con stringhe come nell'esempio, puoi usarlo createElement("div"), impostarne una innerHTMLvolta sul blocco e aggiungere listener secondo necessità. Quindi aggiungi i div come figli dell'elemento container.

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));
}


1
Questo è l'approccio che stavo prendendo in considerazione e darei senza dubbio una soluzione, ma anche l'altra risposta è ottima.
Pogrindis,

Grazie per la risposta, ma ho provato a creare il layout con createElement e non ho avuto successo
Luiz Adorno,

1
Nessun problema. Spero che ti renda conto che se hai un grosso pezzo di HTML, puoi creare un contenitore di elementi radice come un <div>, quindi div.innerHTML += yourHugeChunkOfHTML. Quindi non c'è motivo per cui questo non dovrebbe funzionare per nessun caso d'uso.
ggorlen,

1
Proverò a costruire questo layout con la tua raccomandazione, ho paura di usare "document.getElementById" più volte e rallentare le prestazioni
Luiz Adorno
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.