Liaison d'événement sur des éléments créés dynamiquement?

1677

J'ai un peu de code où je passe en revue toutes les zones de sélection sur une page et leur lie un .hoverévénement pour faire un peu de twiddling avec leur largeur mouse on/off.

Cela se produit sur la page prête et fonctionne très bien.

Le problème que j'ai est que toutes les boîtes de sélection que j'ajoute via Ajax ou DOM après la boucle initiale n'auront pas l'événement lié.

J'ai trouvé ce plugin (plugin jQuery Live Query ), mais avant d'ajouter un autre 5k à mes pages avec un plugin, je veux voir si quelqu'un connaît un moyen de le faire, soit directement avec jQuery, soit par une autre option.

Eli
la source

Réponses:

2251

Depuis jQuery 1.7, vous devez utiliser jQuery.fn.on:

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

Avant cela , l'approche recommandée consistait à utiliser live():

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

Cependant, a live()été déprécié en 1.7 en faveur de on(), et complètement supprimé en 1.9. La live()signature:

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

... peut être remplacé par la on()signature suivante :

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

Par exemple, si votre page créait dynamiquement des éléments avec le nom de classe, dosomethingvous lieriez l'événement à un parent qui existe déjà (c'est le nœud du problème ici, vous avez besoin de quelque chose qui existe pour se lier, ne vous liez pas au contenu dynamique), cela peut être (et l'option la plus simple) l'est document. Cependant, n'oubliez documentpas que l'option n'est pas la plus efficace .

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

Tout parent qui existe au moment où l'événement est lié va bien. Par exemple

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

s'appliquerait à

<div class="buttons">
    <!-- <button>s that are generated dynamically and added here -->
</div>
Popnoodles
la source
7
Notez que la méthode live ne fonctionne que pour certains événements, et pas pour d'autres tels que les métadonnées chargées. (Voir la section mises en garde dans la documentation.)
Sam Dutton
48
En savoir plus sur la délégation d'événements ici: learn.jquery.com/events/event-delegation .
Felix Kling
9
Y a-t-il un moyen d'accomplir cela avec du javascript / vanilla pur?
Ram Patra
15
@Ramswaroop tout ce que vous pouvez faire dans jQuery peut être accompli sans jQuery. Voici un bon exemple de délégation d'événement sans jQuery
Dave
5
@dave Je me demande pourquoi la réponse que vous avez indiquée ne figure pas ici. Eli a clairement demandé une solution sans plugin si possible.
Ram Patra
377

Il y a une bonne explication dans la documentation de jQuery.fn.on.

En bref:

Les gestionnaires d'événements ne sont liés qu'aux éléments actuellement sélectionnés; ils doivent exister sur la page au moment où votre code appelle .on().

Ainsi, dans l'exemple suivant, il #dataTable tbody trdoit exister avant la génération du code.

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

Si un nouveau code HTML est injecté dans la page, il est préférable d'utiliser des événements délégués pour attacher un gestionnaire d'événements, comme décrit ci-dessous.

Les événements délégués ont l'avantage de pouvoir traiter les événements des éléments descendants qui sont ajoutés au document ultérieurement. Par exemple, si la table existe, mais que les lignes sont ajoutées dynamiquement à l'aide de code, les éléments suivants la géreront:

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

En plus de leur capacité à gérer des événements sur des éléments descendants qui ne sont pas encore créés, un autre avantage des événements délégués est leur potentiel de surcharge beaucoup plus faible lorsque de nombreux éléments doivent être surveillés. Sur une table de données contenant 1 000 lignes tbody, le premier exemple de code attache un gestionnaire à 1 000 éléments.

Une approche par événements délégués (le deuxième exemple de code) attache un gestionnaire d'événements à un seul élément tbody, et l'événement n'a besoin de remonter que d'un niveau (du clic trvers tbody).

Remarque: les événements délégués ne fonctionnent pas pour SVG .

Ronen Rabinovici
la source
11
ce serait une réponse mieux acceptée car il serait plus rapide de déléguer à partir du tableau spécifique plutôt que complètement depuis le document (la zone de recherche serait beaucoup plus petite)
msanjay
5
@msanjay: Bien que cibler la recherche plus près des éléments soit préféré, la différence recherche / vitesse est très mineure en pratique. Vous devez cliquer 50 000 fois par seconde pour remarquer quoi que ce soit :)
Gone Coding
2
Cette approche peut-elle être appliquée à la gestion des clics de case à cocher dans une ligne en changeant trpour un sélecteur approprié, comme 'input[type=checkbox]', et cela serait automatiquement géré pour les lignes nouvellement insérées?
JoeBrockhaus
1
Je vous remercie! Cela résout mon problème. Cette simple déclaration était mon problème, "Les gestionnaires d'événements ne sont liés qu'aux éléments actuellement sélectionnés; ils doivent exister sur la page au moment où votre code appelle ..."
Dennis Fazekas
216

Il s'agit d'une solution JavaScript pure sans bibliothèques ni plugins:

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

hasClassest

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

Live demo

Nous remercions Dave et Sime Vidas

L'utilisation de JS plus modernes hasClasspeut être implémentée comme:

function hasClass(elem, className) {
    return elem.classList.contains(className);
}
Ram Patra
la source
6
Vous pouvez utiliser Element.classList au lieu de fractionner
Eugen Konkov
2
@EugenKonkov Element.classListn'est pas pris en charge pris en charge sur les anciens navigateurs. Par exemple, IE <9.
Ram Patra
2
Un bel article sur la façon de faire avancer les choses en utilisant un script vanille au lieu de jQuery - toddmotto.com/…
Ram Patra
2
que diriez-vous de bouillonner? Et si l'événement de clic s'est produit sur un enfant de l'élément qui vous intéresse?
Andreas Trantidis
73

Vous pouvez ajouter des événements aux objets lorsque vous les créez. Si vous ajoutez les mêmes événements à plusieurs objets à des moments différents, la création d'une fonction nommée peut être la voie à suivre.

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 */ )
;
nickf
la source
49

Vous pouvez simplement envelopper votre appel de liaison d'événement dans une fonction, puis l'invoquer deux fois: une fois sur le document prêt et une fois après votre événement qui ajoute les nouveaux éléments DOM. Si vous faites cela, vous voudrez éviter de lier deux fois le même événement aux éléments existants, vous aurez donc besoin de dissocier les événements existants ou (mieux) de ne vous lier qu'aux éléments DOM nouvellement créés. Le code ressemblerait à ceci:

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

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

// ... add elements ...
addCallbacks($(".myNewElements"))
Greg Borenstein
la source
2
Ce message m'a vraiment aidé à comprendre un problème que je rencontrais en chargeant le même formulaire et en obtenant 1,2,4,8,16 ... soumissions. Au lieu d'utiliser .live (), je viens d'utiliser .bind () dans mon rappel .load (). Problème résolu. Merci!
Thomas McCabe
42

Essayez d'utiliser .live()au lieu de .bind(); le .live()sera lié .hoverà votre case à cocher après l'exécution de la demande Ajax.

user670265
la source
32
La méthode a live()été déconseillée dans la version 1.7 au profit de onet supprimée dans la version 1.9.
chridam
27

Liaison d'événement sur des éléments créés dynamiquement

Élément unique:

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

Élément enfant:

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

Remarquez l'ajout *. Un événement sera déclenché pour tous les enfants de cet élément.

Je me suis rendu compte que:

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

Cela ne fonctionne plus, mais cela fonctionnait auparavant. J'utilise jQuery de Google CDN , mais je ne sais pas s'ils l'ont changé.

MadeInDreams
la source
Yeap et ils ne disent pas (document.body) son dit ancêtre qui pourrait être à peu près n'importe quoi
MadeInDreams
25

Vous pouvez utiliser la méthode live () pour lier des éléments (même nouvellement créés) à des événements et des gestionnaires, comme l'événement onclick.

Voici un exemple de code que j'ai écrit, où vous pouvez voir comment la méthode live () lie les éléments choisis, même nouvellement créés, aux événements:

<!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>
Fazi
la source
23

Je préfère utiliser le sélecteur et je l'applique sur le document.

Cela se lie au document et sera applicable aux éléments qui seront rendus après le chargement de la page.

Par exemple:

$(document).on("click", 'selector', function() {
    // Your code here
});
Vatsal
la source
2
le sélecteur ne doit pas être entouré de $, donc le format correct sera $ (document) .on ("click", "selector", function () {// Votre code ici});
pilote automatique le
1
Il est également inutile d'enrouler un objet jQuery autour de la selectorvariable, quand il doit contenir une chaîne ou un objet Element que vous pouvez simplement passer directement à cet argument deon()
Rory McCrossan
20

Une autre solution consiste à ajouter l'écouteur lors de la création de l'élément. Au lieu de placer l'écouteur dans le corps, vous placez l'écouteur dans l'élément au moment où vous le créez:

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

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


function goToGoogle(event){
    window.location.replace("http://www.google.com");
}
Martin Da Rosa
la source
Votre code contient 1 erreur: myElement.append('body');doit l'être myElement.appendTo('body');. D'un autre côté, s'il n'est pas nécessaire d'utiliser davantage la variable, myElementc'est plus facile et plus court de cette façon:$('body').append($('<button/>', { text: 'Go to Google!' }).bind( 'click', goToGoogle));
ddlab
17

Essayez comme ça -

$(document).on( 'click', '.click-activity', function () { ... });
Rohit Suthar
la source
16

Prenez note de la classe "MAIN" l'élément est placé, par exemple,

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

Dans le scénario ci-dessus, l'objet MAIN que jQuery surveillera est "conteneur".

Ensuite , vous aurez essentiellement des noms des éléments sous conteneur tel ul, liet select:

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

Cela se fait par délégation d'événement . L'événement sera lié à l'élément de classe wrapper mais sera délégué à l'élément de classe sélecteur. Voilà comment cela fonctionne.

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

Remarque:

L'élément wrapper-class peut être n'importe quoi ex. document, corps ou votre emballage. Wrapper devrait déjà exister . Cependant, selectorne doit pas nécessairement être présenté au moment du chargement de la page. Cela peut arriver plus tard et l'événement se liera selector sans échec .

Mustkeem K
la source
14

tu pourrais utiliser

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

ou

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

ces deux méthodes sont équivalentes mais ont un ordre de paramètres différent.

voir: événement jQuery Delegate

Mensur Grišević
la source
2
delegate()est désormais obsolète. Ne l'utilise pas.
Rory McCrossan
14

Vous pouvez attacher un événement à l'élément lors de sa création dynamique à l'aide de jQuery(html, attributes).

Depuis jQuery 1.8 , toute méthode d'instance jQuery (une méthode de jQuery.fn) peut être utilisée comme propriété de l'objet passé au deuxième paramètre:

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>

guest271314
la source
11

Voici pourquoi les éléments créés dynamiquement ne répondent pas aux clics:

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>

Pour contourner ce problème, vous devez écouter tous les clics et vérifier l'élément source:

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>

C'est ce qu'on appelle la "délégation d'événement". Bonne nouvelle, c'est une fonctionnalité intégrée à 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>

feuille
la source
10

Tout élément existant au moment où l'événement est lié et si votre page créait dynamiquement des éléments avec le bouton de nom de classe, vous lieriez l'événement à un parent qui existe déjà

$(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>

Ankit Kathiriya
la source
7

Liez l'événement à un parent qui existe déjà:

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

Utilisez la .on()méthode de jQuery http://api.jquery.com/on/ pour attacher des gestionnaires d'événements à l'élément live.

Également à partir de la version 1.9, la .live()méthode est supprimée.

Kalpesh Patel
la source
3

Je préfère que les écouteurs d'événements soient déployés de manière modulaire plutôt que de créer un script pour un documentécouteur d'événements de niveau. Donc, j'aime ci-dessous. Notez que vous ne pouvez pas sursouscrire un élément avec le même écouteur d'événements, alors ne vous inquiétez pas d'attacher un écouteur plus d'une fois - un seul bâton.

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
}

Ronnie Royston
la source
Je préfère cette implémentation; Je dois juste mettre en place un rappel
William
3

Une autre solution flexible pour créer des éléments et lier des événements ( source )

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

Remarque: Cela créera une instance de gestionnaire d'événements pour chaque élément (peut affecter les performances lorsqu'il est utilisé dans des boucles)

Prasad De Silva
la source
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>
Fakhrul Hasan
la source
3
Bien que cet extrait de code puisse résoudre le problème, il n'explique pas pourquoi ni comment il répond à la question. Veuillez inclure une explication pour votre code, car cela aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondrez à la question pour les lecteurs à l'avenir, et ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
Palec
0

Je cherchais une solution pour obtenir $.bindet $.unbindtravailler sans problème dans des éléments ajoutés dynamiquement.

Comme on () fait l'astuce pour attacher des événements, afin de créer un faux lien sur ceux auxquels je suis venu:

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

// unbind the click
$('body').on('click', 'button.send', function(){} );
Evhz
la source
Le déliement ne fonctionne pas, cela ajoute simplement un autre événement qui pointe vers une fonction vide ...
Fabian Bigler