Associazione di eventi su elementi creati dinamicamente?


1677

Ho un po 'di codice in cui sto scorrendo ciclicamente tutte le caselle di selezione su una pagina e rilegando a .hover evento a loro per fare un po' di twiddling con la loro larghezza attiva mouse on/off.

Questo succede sulla pagina pronta e funziona bene.

Il problema che ho è che tutte le caselle selezionate che aggiungo tramite Ajax o DOM dopo il ciclo iniziale non avranno l'evento associato.

Ho trovato questo plug-in ( jQuery Live Query Plugin ), ma prima di aggiungere altri 5k alle mie pagine con un plug-in, voglio vedere se qualcuno conosce un modo per farlo, sia con jQuery direttamente o con un'altra opzione.

Risposte:


2250

A partire da jQuery 1.7 dovresti usare jQuery.fn.on:

$(staticAncestors).on(eventName, dynamicChild, function() {});

Prima di questo , l'approccio raccomandato era di usare live():

$(selector).live( eventName, function(){} );

Tuttavia, è live()stato deprecato in 1,7 a favore di on()e completamente rimosso in 1,9. La live()firma:

$(selector).live( eventName, function(){} );

... può essere sostituito con la seguente on()firma:

$(document).on( eventName, selector, function(){} );

Ad esempio, se la tua pagina creava in modo dinamico elementi con il nome della classe dosomething, associ l'evento a un genitore già esistente (questo è il nocciolo del problema qui, hai bisogno di qualcosa che esiste a cui legarti, non associarti al contenuto dinamico), questa può essere (e l'opzione più semplice) è document. Anche se tenere presente che documentpotrebbe non essere l'opzione più efficiente .

$(document).on('mouseover mouseout', '.dosomething', function(){
    // what you want to happen when mouseover and mouseout 
    // occurs on elements that match '.dosomething'
});

Qualsiasi genitore esistente al momento in cui l'evento è associato va bene. Per esempio

$('.buttons').on('click', 'button', function(){
    // do something here
});

si applicherebbe a

<div class="buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>

7
Si noti che il metodo live funziona solo per determinati eventi e non altri come i metadati caricati. (Vedi la sezione avvertimenti nella documentazione.)
Sam Dutton,

48
Ulteriori informazioni sulla delega degli eventi qui: learn.jquery.com/events/event-delegation .
Felix Kling

9
Un modo per ottenere questo risultato con javascript / vanilla js puro?
Ram Patra,

15
@Ramswaroop tutto ciò che puoi fare in jQuery può essere realizzato senza jQuery. Ecco un buon esempio di delega di eventi senza jQuery
Dave

5
@dave Mi chiedo perché la risposta che hai indicato non sia elencata qui. Eli ha chiaramente chiesto una soluzione senza alcun plug-in, se possibile.
Ram Patra,

377

C'è una buona spiegazione nella documentazione di jQuery.fn.on.

In breve:

I gestori di eventi sono associati solo agli elementi attualmente selezionati; devono esistere sulla pagina nel momento in cui il codice effettua la chiamata .on().

Pertanto, nell'esempio seguente #dataTable tbody trdeve esistere prima che venga generato il codice.

$("#dataTable tbody tr").on("click", function(event){
    console.log($(this).text());
});

Se viene iniettato nuovo HTML nella pagina, è preferibile utilizzare eventi delegati per allegare un gestore eventi, come descritto di seguito.

Gli eventi delegati hanno il vantaggio di poter elaborare eventi da elementi discendenti che vengono aggiunti al documento in un secondo momento. Ad esempio, se la tabella esiste, ma le righe vengono aggiunte in modo dinamico utilizzando il codice, verrà gestito da quanto segue:

$("#dataTable tbody").on("click", "tr", function(event){
    console.log($(this).text());
});

Oltre alla loro capacità di gestire eventi su elementi discendenti che non sono ancora stati creati, un altro vantaggio degli eventi delegati è il loro potenziale per un sovraccarico molto più basso quando molti elementi devono essere monitorati. Su una tabella di dati con 1.000 righe al suo interno tbody, il primo esempio di codice collega un gestore a 1.000 elementi.

Un approccio con eventi delegati (il secondo esempio di codice) collega un gestore di eventi a un solo elemento, tbodyl'evento e deve solo passare a un livello (dal clic tra tbody).

Nota: gli eventi delegati non funzionano per SVG .


11
questa sarebbe una risposta meglio accettata perché sarebbe più veloce delegare dalla tabella specifica piuttosto che dal documento (l'area di ricerca sarebbe molto più piccola)
msanjay

5
@msanjay: sebbene sia preferibile orientare la ricerca più vicino agli elementi, la differenza di ricerca / velocità è in pratica molto ridotta. Dovresti fare clic 50.000 volte al secondo per notare qualcosa :)
Codice andato il

2
Questo approccio può essere applicato alla gestione dei clic delle caselle di controllo in una riga passando tra un selettore appropriato, come 'input[type=checkbox]', e questo verrebbe gestito automaticamente per le righe appena inserite?
JoeBrockhaus,

1
Grazie! Questo risolve il mio problema. Questa semplice affermazione era il mio problema, "I gestori di eventi sono associati solo agli elementi attualmente selezionati; devono esistere sulla pagina nel momento in cui il codice effettua la chiamata a ..."
Dennis Fazekas,

216

Questa è una soluzione JavaScript pura senza librerie o plugin:

document.addEventListener('click', function (e) {
    if (hasClass(e.target, 'bu')) {
        // .bu clicked
        // Do your thing
    } else if (hasClass(e.target, 'test')) {
        // .test clicked
        // Do your other thing
    }
}, false);

dove hasClassè

function hasClass(elem, className) {
    return elem.className.split(' ').indexOf(className) > -1;
}

Live demo

Il merito va a Dave e Sime Vidas

Utilizzando JS più moderni, hasClasspuò essere implementato come:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}


6
Puoi usare Element.classList invece di dividere
Eugen Konkov

2
@EugenKonkov Element.classListnon è supportato supportato su browser meno recenti. Ad esempio, IE <9.
Ram Patra

2
Un bell'articolo su come fare le cose usando lo script alla vaniglia invece di jQuery - toddmotto.com/…
Ram Patra,

2
che ne dici di gorgogliare? Cosa succede se l'evento click si verifica su un figlio dell'elemento a cui sei interessato?
Andreas Trantidis,

73

È possibile aggiungere eventi agli oggetti quando vengono creati. Se si aggiungono gli stessi eventi a più oggetti in momenti diversi, la creazione di una funzione con nome potrebbe essere la strada da percorrere.

var mouseOverHandler = function() {
    // Do stuff
};
var mouseOutHandler = function () {
    // Do stuff
};

$(function() {
    // On the document load, apply to existing elements
    $('select').hover(mouseOverHandler, mouseOutHandler);
});

// This next part would be in the callback from your Ajax call
$("<select></select>")
    .append( /* Your <option>s */ )
    .hover(mouseOverHandler, mouseOutHandler)
    .appendTo( /* Wherever you need the select box */ )
;

49

Potresti semplicemente racchiudere il richiamo del tuo evento in una funzione e quindi richiamarlo due volte: una volta sul documento pronto e una volta dopo l'evento che aggiunge i nuovi elementi DOM. Se lo fai, ti consigliamo di evitare di associare due volte lo stesso evento agli elementi esistenti, quindi dovrai separare gli eventi esistenti o (meglio) associare solo gli elementi DOM che sono stati appena creati. Il codice sarebbe simile al seguente:

function addCallbacks(eles){
    eles.hover(function(){alert("gotcha!")});
}

$(document).ready(function(){
    addCallbacks($(".myEles"))
});

// ... add elements ...
addCallbacks($(".myNewElements"))

2
Questo post mi ha davvero aiutato a capire un problema che stavo caricando lo stesso modulo e ottenendo 1,2,4,8,16 ... invii. Invece di usare .live () ho appena usato .bind () nel mio callback .load (). Problema risolto. Grazie!
Thomas McCabe,

42

Prova a usare .live()invece di .bind(); la .live()si legano .hoveralla tua casella di controllo dopo la richiesta Esegue Ajax.


32
Il metodo è live()stato deprecato nella versione 1.7 a favore one eliminato nella versione 1.9.
chridam,

27

Associazione di eventi su elementi creati dinamicamente

Singolo elemento:

$(document.body).on('click','.element', function(e) {  });

Elemento figlio:

 $(document.body).on('click','.element *', function(e) {  });

Notare l'aggiunta * . Verrà attivato un evento per tutti i figli di quell'elemento.

Ho notato che:

$(document.body).on('click','.#element_id > element', function(e) {  });

Non funziona più, ma prima funzionava. Sto usando jQuery da Google CDN , ma non so se l'hanno cambiato.


Yeap e non stanno dicendo (document.body) che dice antenato che potrebbe essere praticamente qualsiasi cosa
MadeInDreams,

25

È possibile utilizzare il metodo live () per associare elementi (anche quelli appena creati) a eventi e gestori, come l'evento onclick.

Ecco un codice di esempio che ho scritto, in cui puoi vedere come il metodo live () associa gli elementi scelti, anche quelli appena creati, agli eventi:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Untitled Document</title>
    </head>

    <body>
        <script src="http://code.jquery.com/jquery-latest.js"></script>
        <script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/jquery-ui.min.js"></script>

        <input type="button" id="theButton" value="Click" />
        <script type="text/javascript">
            $(document).ready(function()
                {
                    $('.FOO').live("click", function (){alert("It Works!")});
                    var $dialog = $('<div></div>').html('<div id="container"><input type ="button" id="CUSTOM" value="click"/>This dialog will show every time!</div>').dialog({
                                                                                                         autoOpen: false,
                                                                                                         tite: 'Basic Dialog'
                                                                                                     });
                    $('#theButton').click(function()
                    {
                        $dialog.dialog('open');
                        return('false');
                    });
                    $('#CUSTOM').click(function(){
                        //$('#container').append('<input type="button" value="clickmee" class="FOO" /></br>');
                        var button = document.createElement("input");
                        button.setAttribute('class','FOO');
                        button.setAttribute('type','button');
                        button.setAttribute('value','CLICKMEE');
                        $('#container').append(button);
                    });
                    /* $('#FOO').click(function(){
                                                     alert("It Works!");
                                                 }); */
            });
        </script>
    </body>
</html>

23

Preferisco usare il selettore e lo applico sul documento.

Questo si lega al documento e sarà applicabile agli elementi che verranno renderizzati dopo il caricamento della pagina.

Per esempio:

$(document).on("click", 'selector', function() {
    // Your code here
});

2
il selettore non dovrebbe essere racchiuso tra $, quindi il formato corretto sarà $ (documento) .on ("clic", "selettore", funzione () {// Il tuo codice qui});
pilota automatico

1
È anche inutile avvolgere un oggetto jQuery attorno alla selectorvariabile, quando deve contenere una stringa o un oggetto Element che puoi semplicemente passare direttamente all'argomento dion()
Rory McCrossan,

20

Un'altra soluzione è aggiungere l'ascoltatore durante la creazione dell'elemento. Invece di mettere l'ascoltatore nel corpo, inserisci l'ascoltatore nell'elemento nel momento in cui lo crei:

var myElement = $('<button/>', {
    text: 'Go to Google!'
});

myElement.bind( 'click', goToGoogle);
myElement.append('body');


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}

Il tuo codice contiene 1 errore: myElement.append('body');deve essere myElement.appendTo('body');. D'altra parte, se non è necessario un ulteriore uso della variabile myElement, è più facile e più breve in questo modo:$('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
ddlab,

17

Prova in questo modo -

$(document).on( 'click', '.click-activity', function () { ... });

16

Prendi nota della classe "MAIN" in cui viene posizionato l'elemento, ad esempio

<div class="container">
     <ul class="select">
         <li> First</li>
         <li>Second</li>
    </ul>
</div>

Nello scenario sopra, l'oggetto MAIN che jQuery guarderà è "container".

Poi si dovranno essenzialmente elementi di nomi sotto contenitore come ul, li, e select:

$(document).ready(function(e) {
    $('.container').on( 'click',".select", function(e) {
        alert("CLICKED");
    });
 });

15

Questo viene fatto dalla delegazione degli eventi . L'evento verrà associato all'elemento di classe wrapper ma verrà delegato all'elemento di classe selettore. Ecco come funziona.

$('.wrapper-class').on("click", '.selector-class', function() {
    // Your code here
});

Nota:

L'elemento di classe wrapper può essere qualsiasi cosa ex. documento, corpo o wrapper. Wrapper dovrebbe già esistere . Tuttavia, selectornon deve necessariamente essere presentato al momento del caricamento della pagina. Potrebbe venire più tardi e l'evento si legherà selector senza fallo .


14

potresti usare

$('.buttons').on('click', 'button', function(){
    // your magic goes here
});

o

$('.buttons').delegate('button', 'click', function() {
    // your magic goes here
});

questi due metodi sono equivalenti ma hanno un diverso ordine di parametri.

vedi: Evento delegato jQuery


2
delegate()è ora obsoleto. Non usarlo.
Rory McCrossan,

14

È possibile associare un evento all'elemento quando creato dinamicamente utilizzando jQuery(html, attributes).

A partire da jQuery 1.8 , qualsiasi metodo di istanza jQuery (un metodo di jQuery.fn) può essere utilizzato come proprietà dell'oggetto passato al secondo parametro:

function handleDynamicElementEvent(event) {
  console.log(event.type, this.value)
}
// create and attach event to dynamic element
jQuery("<select>", {
    html: $.map(Array(3), function(_, index) {
      return new Option(index, index)
    }),
    on: {
      change: handleDynamicElementEvent
    }
  })
  .appendTo("body");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>


11

Ecco perché gli elementi creati dinamicamente non rispondono ai clic:

var body = $("body");
var btns = $("button");
var btnB = $("<button>B</button>");
// `<button>B</button>` is not yet in the document.
// Thus, `$("button")` gives `[<button>A</button>]`.
// Only `<button>A</button>` gets a click listener.
btns.on("click", function () {
  console.log(this);
});
// Too late for `<button>B</button>`...
body.append(btnB);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Per ovviare a questo problema, devi ascoltare tutti i clic e controllare l'elemento sorgente:

var body = $("body");
var btnB = $("<button>B</button>");
var btnC = $("<button>C</button>");
// Listen to all clicks and
// check if the source element
// is a `<button></button>`.
body.on("click", function (ev) {
  if ($(ev.target).is("button")) {
    console.log(ev.target);
  }
});
// Now you can add any number
// of `<button></button>`.
body.append(btnB);
body.append(btnC);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Questo si chiama "Delega degli eventi". Buone notizie, è una funzionalità integrata in jQuery :-)

var i = 11;
var body = $("body");
body.on("click", "button", function () {
  var letter = (i++).toString(36).toUpperCase();
  body.append($("<button>" + letter + "</button>"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>


10

Qualsiasi elemento esistente al momento dell'evento è associato e se la tua pagina creava elementi in modo dinamico con il pulsante Nome classe , associ l'evento a un elemento principale già esistente

$(document).ready(function(){
  //Particular Parent chield click
  $(".buttons").on("click","button",function(){
    alert("Clicked");
  });  
  
  //Dynamic event bind on button class  
  $(document).on("click",".button",function(){
    alert("Dymamic Clicked");
  });
  $("input").addClass("button");  
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="buttons">
  <input type="button" value="1">
  <button>2</button>
  <input type="text">
  <button>3</button>  
  <input type="button" value="5">  
  </div>
<button>6</button>


7

Associa l'evento a un genitore già esistente:

$(document).on("click", "selector", function() {
    // Your code here
});


3

Preferisco che i listener di eventi siano distribuiti in modo modulare piuttosto che creare script per un documentlistener di eventi di livello. Quindi, mi piace di seguito. Nota, non puoi sottoscrivere un elemento con lo stesso listener di eventi, quindi non preoccuparti di associare un listener più di una volta, solo uno.

var iterations = 4;
var button;
var body = document.querySelector("body");

for (var i = 0; i < iterations; i++) {
    button = document.createElement("button");
    button.classList.add("my-button");
    button.appendChild(document.createTextNode(i));
    button.addEventListener("click", myButtonWasClicked);
    body.appendChild(button);
}

function myButtonWasClicked(e) {
    console.log(e.target); //access to this specific button
}


Preferisco questa implementazione; Devo solo richiamare una chiamata
William,

3

Un'altra soluzione flessibile per creare elementi e associare eventi ( fonte )

// creating a dynamic element (container div)
var $div = $("<div>", {id: 'myid1', class: 'myclass'});

//creating a dynamic button
 var $btn = $("<button>", { type: 'button', text: 'Click me', class: 'btn' });

// binding the event
 $btn.click(function () { //for mouseover--> $btn.on('mouseover', function () {
    console.log('clicked');
 });

// append dynamic button to the dynamic container
$div.append($btn);

// add the dynamically created element(s) to a static element
$("#box").append($div);

Nota: questo creerà un'istanza del gestore eventi per ciascun elemento (potrebbe influire sulle prestazioni quando utilizzato nei loop)


0
<html>
    <head>
        <title>HTML Document</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    </head>

    <body>
        <div id="hover-id">
            Hello World
        </div>

        <script>
            jQuery(document).ready(function($){
                $(document).on('mouseover', '#hover-id', function(){
                    $(this).css('color','yellowgreen');
                });

                $(document).on('mouseout', '#hover-id', function(){
                    $(this).css('color','black');
                });
            });
        </script>
    </body>
</html>

3
Sebbene questo frammento di codice possa risolvere il problema, non spiega perché o come risponde alla domanda. Includi una spiegazione per il tuo codice, in quanto ciò aiuta davvero a migliorare la qualità del tuo post. Ricorda che in futuro stai rispondendo alla domanda dei lettori e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
Palec,

0

Stavo cercando una soluzione per ottenere $.binde $.unbindlavorare senza problemi in elementi aggiunti dinamicamente.

Come on () rende il trucco per allegare eventi, al fine di creare un falso distacco su quelli a cui sono arrivato:

const sendAction = function(e){ ... }
// bind the click
$('body').on('click', 'button.send', sendAction );

// unbind the click
$('body').on('click', 'button.send', function(){} );

Il non-legame non funziona, questo aggiunge semplicemente un altro evento che punta a una funzione vuota ...
Fabian Bigler
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.