Allega eventi a elementi dinamici in javascript


92

Sto cercando di inserire dati html dinamicamente in un elenco creato dinamicamente, ma quando provo ad allegare un evento onclick per il pulsante creato dinamicamente l'evento non viene attivato. La soluzione sarebbe davvero apprezzata.

Codice Javascript:

document.addEventListener('DOMContentLoaded', function () {
document.getElementById('btnSubmit').addEventListener('click', function () {
    var name = document.getElementById('txtName').value;
    var mobile = document.getElementById('txtMobile').value;
    var html = '<ul>';
    for (i = 0; i < 5; i++) {
        html = html + '<li>' + name + i + '</li>';
    }
    html = html + '</ul>';

    html = html + '<input type="button" value="prepend" id="btnPrepend" />';
    document.getElementsByTagName('form')[0].insertAdjacentHTML('afterend', html);
});

document.getElementById('btnPrepend').addEventListener('click', function () {
    var html = '<li>Prepending data</li>';
    document.getElementsByTagName('ul')[0].insertAdjacentHTML('afterbegin', html);
});

});

Codice HTML:

<form>
    <div class="control">
        <label>Name</label>
        <input id="txtName" name="txtName" type="text" />
    </div>
    <div class="control">
        <label>Mobile</label>
        <input id="txtMobile" type="text" />
    </div>
    <div class="control">
        <input id="btnSubmit" type="button" value="submit" />
    </div>
</form>

Come stai creando l'html?
jim0thy

1
Direi che è perché l'elemento non esiste quando si tenta di allegare il listener di eventi. - dai un'occhiata a questo learn.jquery.com/events/event-delegation
Craicerjack

1
Sposta il tuo addEventListener nel listener di eventi di btnSubmit
slebetman

Risposte:


211

Ciò è dovuto al fatto che il tuo elemento viene creato dinamicamente. È necessario utilizzare la delega degli eventi per gestire l'evento.

 document.addEventListener('click',function(e){
    if(e.target && e.target.id== 'brnPrepend'){
          //do something
     }
 });

jquery lo rende più facile:

 $(document).on('click','#btnPrepend',function(){//do something})

Di seguito è riportato un articolo sull'articolo sulla delega di eventi sulla delega degli eventi


1
Grazie! A volte la soluzione JS pura non funziona su iphone, ma se cambia documento su un altro div genitore, ad esempio, funziona. Qual è la migliore soluzione per la delega?
Kholiavko

3
@Kholiavko direi che finché l'elemento "genitore" non viene creato dinamicamente, dovrebbe funzionare. Vorrei associare il gestore di eventi al primo genitore dell'elemento d di creazione dinamico in modo che non ci siano conflitti tra i diversi gestori di eventi
jilykate

1
Ma a volte non funziona su iPhone, anche l'elemento "genitore" non viene creato dinamicamente. Ecco exapmle , funziona su tutti i browser, ma non lo fa su iPhone (cromo Safary e persino). E non capisco perché.
Kholiavko

2
@Kholiavko questo è solo perché codepen usa una sandbox per avvolgere tutto. Se scrivi davvero quel codice in una pagina normale, funzionerà
jilykate

2
Considera l'aggiunta di un segno di uguale in più (===) per evitare una coercizione inaspettata di tipo
Staccato

19

Esiste una soluzione alternativa acquisendo i clic document.bodye quindi controllando il target dell'evento.

document.body.addEventListener( 'click', function ( event ) {
  if( event.srcElement.id == 'btnSubmit' ) {
    someFunc();
  };
} );

2
O il container più vicino
mplungjan

Risposta perfetta!
Rahul Purohit

WOW, è fantastico, fa risparmiare così tanto tempo e codice! :)
Anna Medyukh

3
Nota: event.srcElement è deprecato. Si consiglia invece di utilizzare event.target.
dsanchez

12

Devi allegare l'evento dopo aver inserito gli elementi, in questo modo non alleghi un evento globale al tuo documentma un evento specifico agli elementi inseriti.

per esempio

document.getElementById('form').addEventListener('submit', function(e) {
  e.preventDefault();
  var name = document.getElementById('txtName').value;
  var idElement = 'btnPrepend';
  var html = `
    <ul>
      <li>${name}</li>
    </ul>
    <input type="button" value="prepend" id="${idElement}" />
  `;
  /* Insert the html into your DOM */
  insertHTML('form', html);
  /* Add an event listener after insert html */
  addEvent(idElement);
});

const insertHTML = (tag = 'form', html, position = 'afterend', index = 0) => {
  document.getElementsByTagName(tag)[index].insertAdjacentHTML(position, html);
}
const addEvent = (id, event = 'click') => {
  document.getElementById(id).addEventListener(event, function() {
    insertHTML('ul', '<li>Prepending data</li>', 'afterbegin')
  });
}
<form id="form">
  <div>
    <label for="txtName">Name</label>
    <input id="txtName" name="txtName" type="text" />
  </div>
  <input type="submit" value="submit" />
</form>


2
Questo approccio funzionerà solo per CSR (Client Side Rendering). Questo non funzionerà per SSR (Server-Side Rendering). Per farlo funzionare sia per CSR che per SSR, Event Delegation è la soluzione:javascript document.addEventListener('click',function(e){ if(e.target && e.target.id== 'brnPrepend'){ //do something } });
Yatendra Goel

Ma per motivi di prestazioni, questo approccio è migliore. Quindi dipende dal tuo caso d'uso.
R3tep

8

La differenza sta nel modo in cui crei e aggiungi elementi nel DOM.

Se crei un elemento tramite document.createElement, aggiungi un listener di eventi e aggiungilo al DOM. I tuoi eventi verranno attivati.

Se crei un elemento come una stringa come questa: html + = "<li> test </li>" ", l'elemento è tecnicamente solo una stringa. Le stringhe non possono avere listener di eventi.

Una soluzione è creare ogni elemento con document.createElemente quindi aggiungerli direttamente a un elemento DOM.

// Sample
let li = document.createElement('li')
document.querySelector('ul').appendChild(li)

3

Puoi fare qualcosa di simile a questo:

// Get the parent to attatch the element into
var parent = document.getElementsByTagName("ul")[0];

// Create element with random id
var element = document.createElement("li");
element.id = "li-"+Math.floor(Math.random()*9999);

// Add event listener
element.addEventListener("click", EVENT_FN);

// Add to parent
parent.appendChild(element);

2
var __ = function(){
    this.context  = [];
    var self = this;
    this.selector = function( _elem, _sel ){
        return _elem.querySelectorAll( _sel );
    }
          this.on = function( _event, _element, _function ){
              this.context = self.selector( document, _element );
              document.addEventListener( _event, function(e){
                  var elem = e.target;
                  while ( elem != null ) {
                      if( "#"+elem.id == _element || self.isClass( elem, _element ) || self.elemEqal( elem ) ){
                          _function( e, elem );
                      }
                      elem = elem.parentElement;
                  }
              }, false );
     };

     this.isClass = function( _elem, _class ){
        var names = _elem.className.trim().split(" ");
        for( this.it = 0; this.it < names.length; this.it++ ){
            names[this.it] = "."+names[this.it];
        }
        return names.indexOf( _class ) != -1 ? true : false;
    };

    this.elemEqal = function( _elem ){
        var flg = false;
        for( this.it = 0; this.it < this.context.length;  this.it++ ){
            if( this.context[this.it] === _elem && !flg ){
                flg = true;
            }
        }
        return flg;
    };

}

    function _( _sel_string ){
        var new_selc = new __( _sel_string );
        return new_selc;
    }

Ora puoi registrare eventi come,

_( document ).on( "click", "#brnPrepend", function( _event, _element ){
      console.log( _event );
      console.log( _element );
      // Todo

  });

Supporto browser

chrome - 4.0, Edge - 9.0, Firefox - 3.5 Safari - 3.2, Opera - 10.0 e versioni successive


2

Ho creato una piccola libreria per aiutare con questo: Fonte della libreria su GitHub

<script src="dynamicListener.min.js"></script>
<script>
// Any `li` or element with class `.myClass` will trigger the callback, 
// even elements created dynamically after the event listener was created.
addDynamicEventListener(document.body, 'click', '.myClass, li', function (e) {
    console.log('Clicked', e.target.innerText);
});
</script>

La funzionalità è simile a jQuery.on ().

La libreria utilizza il metodo Element.matches () per testare l'elemento di destinazione rispetto al selettore specificato. Quando viene attivato un evento, la richiamata viene chiamata solo se l'elemento di destinazione corrisponde al selettore fornito.


0

So che l'argomento è troppo vecchio ma mi sono dato alcuni minuti per creare un codice molto utile che funziona bene e molto facile da usare puro JAVASCRIPT. Ecco il codice con un semplice esempio:

String.prototype.addEventListener=function(eventHandler, functionToDo){
  let selector=this;
  document.body.addEventListener(eventHandler, function(e){
    e=(e||window.event);
    e.preventDefault();
    const path=e.path;
    path.forEach(function(elem){
      const selectorsArray=document.querySelectorAll(selector);
      selectorsArray.forEach(function(slt){
        if(slt==elem){
          if(typeof functionToDo=="function") functionToDo(el=slt, e=e);
        }
      });
    });
  });
}

// And here is how we can use it actually !

"input[type='number']".addEventListener("click", function(element, e){
	console.log( e ); // Console log the value of the current number input
});
<input type="number" value="25">
<br>
<input type="number" value="15">
<br><br>
<button onclick="addDynamicInput()">Add a Dynamic Input</button>
<script type="text/javascript">
  function addDynamicInput(){
    const inpt=document.createElement("input");
          inpt.type="number";
          inpt.value=Math.floor(Math.random()*30+1);
    document.body.prepend(inpt);
  }
</script>


0

Ho creato una semplice funzione per questo.

La _casefunzione ti consente non solo di ottenere il target, ma anche di ottenere l'elemento genitore a cui leghi l'evento.

La funzione di callback restituisce l'evento che contiene target ( evt.target) e l'elemento genitore che corrisponde al selector ( this). Qui puoi fare le cose che ti servono dopo aver cliccato sull'elemento.

Non ho ancora deciso quale sia il migliore, il if-elseo ilswitch

var _case = function(evt, selector, cb) {
  var _this = evt.target.closest(selector);
  if (_this && _this.nodeType) {
    cb.call(_this, evt);
    return true;
  } else { return false; }
}

document.getElementById('ifelse').addEventListener('click', function(evt) {
  if (_case(evt, '.parent1', function(evt) {
      console.log('1: ', this, evt.target);
    })) return false;

  if (_case(evt, '.parent2', function(evt) {
      console.log('2: ', this, evt.target);
    })) return false;

  console.log('ifelse: ', this);
})


document.getElementById('switch').addEventListener('click', function(evt) {
  switch (true) {
    case _case(evt, '.parent3', function(evt) {
      console.log('3: ', this, evt.target);
    }): break;
    case _case(evt, '.parent4', function(evt) {
      console.log('4: ', this, evt.target);
    }): break;
    default:
      console.log('switch: ', this);
      break;
  }
})
#ifelse {
  background: red;
  height: 100px;
}
#switch {
  background: yellow;
  height: 100px;
}
<div id="ifelse">
  <div class="parent1">
    <div class="child1">Click me 1!</div>
  </div>
  <div class="parent2">
    <div class="child2">Click me 2!</div>
  </div>
</div>

<div id="switch">
  <div class="parent3">
    <div class="child3">Click me 3!</div>
  </div>
  <div class="parent4">
    <div class="child4">Click me 4!</div>
  </div>
</div>

Spero che sia d'aiuto!


0

Ho trovato la soluzione pubblicata da jillykate funziona, ma solo se l'elemento di destinazione è il più annidato. Se questo non è il caso, questo può essere corretto iterando sui genitori, ad es

function on_window_click(event)
{
    let e = event.target;

    while (e !== null)
    {
        // --- Handle clicks here, e.g. ---
        if (e.getAttribute(`data-say_hello`))
        {
            console.log("Hello, world!");
        }

        e = e.parentElement;
    }
}

window.addEventListener("click", on_window_click);

Nota anche che possiamo gestire gli eventi con qualsiasi attributo o collegare il nostro ascoltatore a qualsiasi livello. Il codice precedente utilizza un attributo personalizzato e window. Dubito che ci sia una differenza pragmatica tra i vari metodi.

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.