jQuery .live () vs .on () méthode pour ajouter un événement de clic après le chargement de HTML dynamique

216

J'utilise jQuery v.1.7.1 où la méthode .live () est apparemment obsolète.

Le problème que j'ai est que lors du chargement dynamique de code HTML dans un élément à l'aide de:

$('#parent').load("http://..."); 

Si j'essaie d'ajouter un événement de clic par la suite, il n'enregistre pas l'événement à l'aide de l'une des méthodes suivantes:

$('#parent').click(function() ...); 

ou

// according to documentation this should be used instead of .live()
$('#child').on('click', function() ...); 

Quelle est la bonne façon d'obtenir cette fonctionnalité? Cela ne semble fonctionner qu'avec .live () pour moi, mais je ne devrais pas utiliser cette méthode. Notez que #child est un élément chargé dynamiquement.

Merci.

Sean Thoman
la source
25
Pourquoi dites-vous "prétendument obsolète" ? Tu ne crois pas aux docs?
6
Ce n'est pas censé être obsolète: il est obsolète. Si vous regardez le doco jQuery,.live() il vous indique comment réécrire les utilisations existantes de .live()à utiliser .delegate()ou .on()(selon que vous êtes sur la version 1.7+ ou non). Notez cependant que si vous ajoutez un gestionnaire avec .click()"après" comme vous le mentionnez, c'est-à-dire après avoir chargé dynamiquement des éléments, cela devrait fonctionner - le seul problème est d'essayer d'assigner avec .click() avant de charger dynamiquement des éléments.
nnnnnn
J'ai changé le libellé en «apparemment» puisque c'est essentiellement ce que je voulais dire. Quoi qu'il en soit, je comprends maintenant que, puisque l'événement .load () est asynchrone, l'élément #child ne peut être reconnu de manière fiable que dans le gestionnaire de réussite, ce qui est logique.
Sean Thoman

Réponses:

606

Si vous voulez que le gestionnaire de clics fonctionne pour un élément qui est chargé dynamiquement, vous définissez le gestionnaire d'événements sur un objet parent (qui n'est pas chargé dynamiquement) et lui donnez un sélecteur qui correspond à votre objet dynamique comme ceci:

$('#parent').on("click", "#child", function() {});

Le gestionnaire d'événements sera attaché à l' #parentobjet et chaque fois qu'un événement de clic se propage à lui #child, il déclenchera votre gestionnaire de clics. C'est ce qu'on appelle la gestion des événements déléguée (la gestion des événements est déléguée à un objet parent).

C'est fait de cette façon parce que vous pouvez attacher l'événement à l' #parentobjet même lorsque l' #childobjet n'existe pas encore, mais quand il existe plus tard et qu'il est cliqué dessus, l'événement click va bouillonner jusqu'à l' #parentobjet, il verra qu'il est originaire #childet il y a un gestionnaire d'événements pour un clic #childet déclencher votre événement.

jfriend00
la source
23
Grande explication! J'ai jamais pu envelopper ma tête live()contre on()mais ce soir j'ai décidé encore une fois pour essayer, et votre explication révèle immédiatement ce que j'avais été absent tout au long. Merci!
MikeSchinkel
6
Un million de fois . J'ai commencé à utiliser knockout avant de réaliser que cela était possible dans jQuery pour les enfants créés dynamiquement, merci beaucoup.
Brock Hensley
9
Vous devriez envisager d'écrire un livre ou quelque chose: votre petit texte m'a été plus utile que la pleine page dans les documents jQuery sur « on () ». Merci beaucoup!
Cholestérol
@ jfriend00 savez-vous par hasard comment nous pouvons appliquer ce même processus à une situation de vol stationnaire, sans vol stationnaire? y a-t-il une source qui en parle?
klewis
@blackhawk - Voir cette réponse . Vous pouvez vous inscrire pour les mouseenteret les mouseleaveévénements et les tests dans le gestionnaire lequel a été déclenché. L'événement pseudo "survol" de jQuery n'est pas disponible avec la délégation.
jfriend00
33

Essaye ça:

$('#parent').on('click', '#child', function() {
    // Code
});

De la $.on()documentation:

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().

Votre #childélément n'existe pas lorsque vous l'appelez $.on(), donc l'événement n'est pas lié (contrairement $.live()). #parentCependant, ne exist, liant donc l'événement qui est très bien.

Le deuxième argument de mon code ci-dessus agit comme un «filtre» pour ne se déclencher que si l'événement s'est propagé à #parentpartir de #child.

Bojangles
la source
Si j'appelle la méthode $ .on () après la méthode $ .load (), alors pourquoi l'élément #child n'existerait-il pas à ce stade?
Sean Thoman
6
@SeanThoman - vous devez l'appeler à partir du gestionnaire de réussite de la .load()méthode - pas à partir du code après la .load()méthode. #childest uniquement connu pour être chargé lorsque le gestionnaire de réussite s'exécute réellement et pas avant.
jfriend00
et même alors, le temps qu'il faut pour insérer toutes les données que vous avez récupérées dans le DOM signifie qu'il pourrait ne pas se connecter. J'ai fait beaucoup de travail sur une seule page récemment et ma norme est var $ body = $ ('body'); $ body.on ("événement", ".élément / # classe", fonction (e) {}); pour tout. 1 sélecteur. Ne vous inquiétez pas de ce qui est chargé ou non.
lupos
21

$(document).on('click', '.selector', function() { /* do stuff */ });

EDIT: Je fournis un peu plus d'informations sur la façon dont cela fonctionne, parce que ... des mots. Avec cet exemple, vous placez un écouteur sur l'ensemble du document.

Lorsque vous correspondez clickà n'importe quel élément .selector, l'événement bouillonne jusqu'au document principal - tant qu'il n'y a pas d'autres écouteurs qui appellent la event.stopPropagation()méthode - ce qui dépasserait la propagation d'un événement aux éléments parents.

Au lieu de vous lier à un élément ou un ensemble d'éléments spécifique, vous écoutez les événements provenant d'éléments qui correspondent au sélecteur spécifié. Cela signifie que vous pouvez créer un écouteur, une fois, qui correspondra automatiquement aux éléments existants ainsi qu'à tous les éléments ajoutés dynamiquement.

C'est intelligent pour plusieurs raisons, notamment les performances et l'utilisation de la mémoire (dans les applications à grande échelle)

ÉDITER:

De toute évidence, l'élément parent le plus proche que vous pouvez écouter est meilleur, et vous pouvez utiliser n'importe quel élément à la place documenttant que les enfants pour lesquels vous souhaitez surveiller les événements se trouvent dans cet élément parent ... mais cela n'a vraiment rien à voir avec la question.

L422Y
la source
23
Il a essayé $(element).on(...). C'est ça $(document).on(...,element,...). Différentes bêtes.
cHao
@ArnoldRoa oui, de meilleures performances. vous n'aurez pas d'écouteur sur plusieurs enfants, vous n'avez pas à réappliquer l'écouteur chaque fois que le DOM est modifié, et les événements remontent par le DOM par défaut. Exécuter une fois et chaque enfant correspondant au sélecteur déclenchera la fonction donnée lorsque vous cliquez dessus.
L422Y
Le commentaire de @ L422Y ici est très trompeur. Si vous êtes curieux de connaître les implications en termes de performances, jetez un œil au commentaire de @ jfriend00 sur la réponse de @ Jared
Gust van de Wal
@GustvandeWal En quoi est-ce trompeur? Si vous allez émettre cent $ (this) .on (...) vs écouter une fois un événement sur un élément parent, c'est beaucoup plus performant.
L422Y
Vous ne parlez que d'attacher les événements. Les goulots d'étranglement des performances dans le monde réel sont trop d'auditeurs (comme ceux que vous déléguez), pas de grandes quantités d'éléments qui ont besoin d'un événement qui leur est attaché.
Gust van de Wal
12

L'équivalent de .live () en 1.7 ressemble à ceci:

$(document).on('click', '#child', function() ...); 

Fondamentalement, regardez le document pour les événements de clic et filtrez-les pour #enfant.

Jared
la source
Parce que c'est ainsi que le gestionnaire d'événements s'attache au document (comme c'est le cas .live()).
cHao
17
L'une des raisons qui .live()est obsolète maintenant est qu'il est mauvais de placer tous les gestionnaires d'événements en direct sur l'objet document. Les choses peuvent vraiment, vraiment ralentir. Non seulement les événements doivent remonter jusqu'au document, mais vous pouvez avoir de nombreux gestionnaires d'événements à parcourir sur l'objet document. Le principal avantage .on()est que vous pouvez l'attacher à un objet parent qui est beaucoup plus proche de l'objet réel et améliorer considérablement les performances. Donc ... je ne recommanderais PAS d'utiliser .on()avec l'objet document. Il vaut mieux choisir un objet parent plus proche.
jfriend00
Merci pour la clarification. Pour ce que ça vaut, les performances des événements ont été beaucoup améliorées avec on (), donc cela devrait être moins problématique qu'auparavant. Si vous attachez à un parent, je suppose que ce devrait être un parent qui existe sur le document prêt ou vous verriez des problèmes similaires.
Jared
Au pire, cela n'émule la désapprouvée approche; cependant, la capacité de contrôler la délégation est certainement avantageuse. .live()
Dan Lugg
9

Je sais qu'il est un peu tard pour une réponse, mais j'ai créé un polyfill pour la méthode .live (). Je l'ai testé dans jQuery 1.11, et il semble fonctionner assez bien. Je sais que nous sommes censés implémenter la méthode .on () dans la mesure du possible, mais dans les grands projets, où il n'est pas possible de convertir tous les appels .live () en appels équivalents .on () pour une raison quelconque, ce qui suit pourrait travail:

if(jQuery && !jQuery.fn.live) {
    jQuery.fn.live = function(evt, func) {
        $('body').on(evt, this.selector, func);
    }
}

Il suffit de l'inclure après avoir chargé jQuery et avant d'appeler live ().

NSV
la source
5

.on () est pour jQuery version 1.7 et supérieure. Si vous avez une ancienne version, utilisez ceci:

$("#SomeId").live("click",function(){
    //do stuff;
});
Matt Cashatt
la source
8
OP dit "J'utilise jQuery v.1.7.1"
Selvakumar Arumugam
1
@ jfriend - pourquoi ne pas poster votre propre réponse au lieu de diminuer la mienne? J'utilise live () tous les jours avec toutes les versions inférieures à 1,6 et cela fonctionne très bien. Je peux voir que vous êtes doué pour lire la documentation, mais il est parfois plus utile de mettre quelque chose en pratique pour une personne. .live () fonctionne. Période.
Matt Cashatt
5
@MatthewPatrickCashatt - J'ai publié ma propre réponse et je n'ai pas dévalidé la vôtre - n'allez pas faire de telles hypothèses aveugles. Pre 1.7, .delegate()fonctionne beaucoup mieux que .live()ce qui explique pourquoi le doc jQuery le recommande et déconseille .live(). Oui, .live()ça marche toujours, mais les réponses ici sur SO devraient recommander la meilleure façon de faire les choses. J'ai vu cela 20 fois ici sur SO. Si vous publiez du code avec .live()ici sur SO, il sera voté (pas habituellement par moi, sauf s'il y a quelque chose de grave qui ne va pas).
jfriend00
1
Je n'ai pas downvote, mais bien qu'il .live()fonctionne définitivement même dans la version 1.7, vous devriez toujours l'utiliser .delegate()ou .on()parce qu'il .live()est déconseillé pour une bonne raison. Tant que vous notez le changement de syntaxe pour les deux nouvelles fonctions, cela fonctionnera bien.
nnnnnn
1
@ jfriend00- Vous avez absolument raison. Longue journée. Mes excuses.
Matt Cashatt
3

J'ai utilisé «live» dans mon projet mais un de mes amis a suggéré que je devrais utiliser «on» au lieu de live. Et quand j'ai essayé de l'utiliser, j'ai rencontré un problème comme vous.

Sur mes pages, je crée dynamiquement des lignes de tableau de boutons et de nombreux éléments dom. mais quand j'utilise sur la magie a disparu.

Les autres solutions comme l'utiliser comme un enfant appellent simplement vos fonctions à chaque fois à chaque clic. Mais je trouve un moyen d'y arriver à nouveau et voici la solution.

Écrivez votre code comme:

function caller(){
    $('.ObjectYouWntToCall').on("click", function() {...magic...});
}

Appelant appelant (); après avoir créé votre objet dans la page comme celle-ci.

$('<dom class="ObjectYouWntToCall">bla... bla...<dom>').appendTo("#whereeveryouwant");
caller();

De cette façon, votre fonction est appelée alors qu'elle n'est pas censée faire chaque clic sur la page.

Coderx07
la source