Come assegnare callback di eventi iterando una matrice in javascript (jQuery)

Sto generando un elenco non ordinato tramite javascript (usando jQuery). Ogni listino deve ricevere il proprio listener di eventi per l’evento “click”. Tuttavia, ho difficoltà a trovare il callback giusto collegato all’elemento giusto. Un esempio di codice (spogliato) potrebbe chiarire un po ‘le cose:

for(class_id in classs) { callback = function() { this.selectClass(class_id) }; li_item = jQuery('
  • ') .click(callback); }

    In realtà, in questa iterazione sta accadendo di più, ma non pensavo che fosse molto pertinente alla domanda. In ogni caso, ciò che sta accadendo è che la funzione di callback sembra essere referenziata piuttosto che memorizzata (e copiata). Risultato finale? Quando un utente fa clic su uno qualsiasi degli elementi dell’elenco, eseguirà sempre l’azione per l’ ultimo class_id nell’array classs , poiché utilizza la funzione memorizzata in callback in quel punto specifico.

    Ho trovato soluzioni alternative (come l’analisi dell’attributo href in un elemento incluso), ma mi chiedevo se c’è un modo per raggiungere i miei obiettivi in ​​un modo “pulito”. Se il mio approccio è terrificante, per favore dimmelo, se mi dici perché 🙂 Grazie!

    Questo è un modo migliore per fare ciò che vuoi.

    Aggiungi le informazioni class_id all’elemento usando .data ().

    Quindi usa .live () per aggiungere un gestore di clic a tutti i nuovi elementi, questo evita di avere funzioni di x * click.

     for(class_id in classs) { li_item = jQuery('
  • ').data('class_id', class_id).addClass('someClass'); } //setup click handler on new li's $('li.someClass').live('click', myFunction ) function myFunction(){ //get class_id var classId = $(this).data('class_id'); //do something }

    Questo è un classico problema “hai bisogno di una chiusura”. Ecco come di solito si gioca.

    1. Iterate su alcuni valori
    2. Definisci / assegna una funzione in quella iterazione che utilizza variabili iterate
    3. Si impara che ogni funzione utilizza solo i valori dell’ultima iterazione.
    4. WTF?

    Di nuovo, quando vedi questo schema, dovrebbe immediatamente farti pensare “chiusura”

    Estendendo il tuo esempio, ecco come avresti chiuso

     for ( class_id in classs ) { callback = function( cid ) { return function() { $(this).selectClass( cid ); } }( class_id ); li_item = jQuery('
  • ').click(callback); }

    Comunque, in questa specifica istanza di jQuery, non dovresti aver bisogno di una chiusura – ma devo chiedere della natura delle tue classs variabili – è un object? Perché si itera su un ciclo for-in, che suggerisce l’object. E per me si pone la domanda, perché non stai memorizzando questo in un array? Perché se lo fosse, il tuo codice potrebbe essere solo questo.

     jQuery('
  • ').click(function() { $(this).addClass( classs.join( ' ' ) ); });

    Il tuo codice:

     for(class_id in classs) { callback = function() { this.selectClass(class_id) }; li_item = jQuery('
  • ') .click(callback); }

    Questo è principalmente ok, solo un problema. La callback variabile è globale; così ogni volta che fai un loop, lo stai sovrascrivendo. Metti la parola chiave var di fronte ad essa per vederla localmente e dovresti stare bene.

    EDIT per commenti: potrebbe non essere globale come dici tu, ma è al di fuori dello scopo del ciclo for. Quindi la variabile è lo stesso riferimento ogni volta intorno al ciclo. Mettere var nel loop lo scopre nel loop, creando ogni volta un nuovo riferimento.

    Il mio javascript fu è piuttosto debole ma, a quanto ho capito, le chiusure fanno riferimento a variabili locali nello stack (e quella cornice dello stack è passata intorno alla funzione, di nuovo, molto approssimativa). Il tuo esempio in effetti non funziona perché ogni funzione mantiene un riferimento alla stessa variabile. Prova invece a creare una funzione diversa che crea la chiusura cioè:

     function createClosure(class_id) { callback = function() { this.selectClass(class_id) }; return callback; } 

    e poi:

     for(class_id in classs) { callback = createClosure(class_id); li_item = jQuery('
  • ').click(callback); }

    Ovviamente è un po ‘scomodo, probabilmente ci sono modi migliori.

    perché non puoi generarli tutti e poi chiamare qualcosa del genere

     $(".li_class").click(function(){ this.whatever() }; 

    MODIFICARE:

    Se è necessario aggiungere più classi, è sufficiente creare una stringa nel proprio ciclo con tutti i nomi di class e utilizzarla come selettore.

     $(".li_class1, .li_class2, etc").click(function(){ this.whatever() }; 

    Oppure puoi albind class_id al .data () di quegli elementi dell’elenco.

     $("
  • ").data("class_id", class_id).click(function(){ alert("This item has class_id "+$(this).data("class_id")); });

    Fai attenzione, però: stai creando nuovamente la funzione di callback per ogni chiamata $("

  • ") . Non sono sicuro dei dettagli di implementazione di JavaScript, ma potrebbe essere costoso. Invece, potresti farlo

     function listItemCallback(){ alert("This item has class_id "+$(this).data("class_id")); } $("
  • ").data("class_id", class_id).click(listItemCallback);