Direct vs délégué - jQuery .on ()

159

Je suis en train de comprendre cette différence particulière entre les directs et délégués des gestionnaires d'événements en utilisant le jQuery .on () méthode . Plus précisément, la dernière phrase de ce paragraphe:

Lorsqu'un selectorest fourni, le gestionnaire d'événements est appelé délégué . Le gestionnaire n'est pas appelé lorsque l'événement se produit directement sur l'élément lié, mais uniquement pour les descendants (éléments internes) qui correspondent au sélecteur. jQuery fait remonter l'événement de la cible de l'événement jusqu'à l'élément auquel le gestionnaire est attaché (c'est-à-dire du plus profond à l'élément le plus externe) et exécute le gestionnaire pour tous les éléments le long de ce chemin correspondant au sélecteur.

Que signifie "exécute le gestionnaire pour tous les éléments"? J'ai fait une page de test pour expérimenter le concept. Mais les deux constructions suivantes conduisent au même comportement:

$("div#target span.green").on("click", function() {
   alert($(this).attr("class") + " is clicked");
});

ou,

$("div#target").on("click", "span.green", function() {
   alert($(this).attr("class") + " is clicked");
});

Peut-être que quelqu'un pourrait se référer à un exemple différent pour clarifier ce point? Merci.

Moey
la source
7
Pour tous les intéressés: jsperf.com/jquery-fn-on-delegate-vs-direct
dgo
1
@KevinWheeler J'ai commenté votre violon ci-dessous mais ici , essentiellement, il n'est pas configuré correctement (vous êtes lié à l'élément parent et la délégation est destinée aux enfants). Pour répondre à votre question moey, cela signifie que le gestionnaire délégué correspondra aux éléments nouvellement ajoutés, contrairement à celui sans délégation. La délégation a l'avantage de réduire le nombre d'événements connectés au navigateur, ce qui entraîne une consommation de mémoire inférieure pour l'application, mais le compromis est qu'elle augmente le temps de traitement d'un clic (au minimum). Si vous créez un jeu, ne déléguez pas.
vipero07
1
La "page de test" à laquelle vous faites référence ne fonctionne pas.
Jaime Montoya

Réponses:

374

Cas 1 (direct):

$("div#target span.green").on("click", function() {...});

== Hé! Je veux que chaque span.green à l'intérieur de div # target écoute: quand on clique dessus, fais X.

Cas 2 (délégué):

$("div#target").on("click", "span.green", function() {...});

== Hé, div # cible! Quand l'un de vos éléments enfants qui sont "span.green" est cliqué, faites X avec eux.

En d'autres termes...

Dans le cas 1, chacune de ces travées a reçu des instructions individuellement. Si de nouvelles plages sont créées, elles n'auront pas entendu l'instruction et ne répondront pas aux clics. Chaque travée est directement responsable de ses propres événements.

Dans le cas 2, seul le conteneur a reçu l'instruction; il est responsable de la détection des clics au nom de ses éléments enfants. Le travail des événements de capture a été délégué . Cela signifie également que l'instruction sera exécutée pour les éléments enfants qui seront créés à l'avenir.

N3dst4
la source
46
C’est une excellente explication, qui a clarifié une question que j’ai depuis longtemps refusé de comprendre. Merci!
dgo
3
Alors, pourquoi on()autorise-t-il deux arguments alors que ce serait à peu près la même chose que l'utilisation click()?
nipponese
5
.on () est une API à usage général qui peut gérer tout type d'événement, y compris plusieurs événements différents (vous pouvez mettre plusieurs noms d'événements dans cette première chaîne.) .click () n'est qu'un raccourci pour ce premier formulaire.
N3dst4
1
@newbie, @ N3dst4: e.targetsera la cible initiale de l'événement de clic (peut être un nœud enfant s'il span.greena des enfants). Depuis l'intérieur du gestionnaire, vous devez utiliser la thisréférence. Voyez ce violon .
LeGEC
1
Une autre note à ajouter au sujet de la délégation - c'est à la fois très utile et très efficace. Vous utiliserez moins de ressources de navigateur avec la délégation par rapport à l'attachement d'un événement à chaque élément correspondant. Je pensais le mentionner, juste au cas où les gens auraient besoin de plus de raisons d'utiliser la délégation.
phatskat
6

La première façon $("div#target span.green").on(),, lie un gestionnaire de clic directement à la ou aux plages qui correspondent au sélecteur au moment où le code est exécuté. Cela signifie que si d'autres intervalles sont ajoutés plus tard (ou ont leur classe modifiée pour correspondre), ils ont manqué et n'auront pas de gestionnaire de clics. Cela signifie également que si vous supprimez plus tard la classe "verte" de l'une des plages, son gestionnaire de clics continuera à s'exécuter - jQuery ne garde pas trace de la façon dont le gestionnaire a été affecté et vérifie si le sélecteur correspond toujours.

La deuxième façon $("div#target").on(),, lie un gestionnaire de clics aux div (s) qui correspondent (encore une fois, c'est contre ceux qui correspondent à ce moment), mais quand un clic se produit quelque part dans le div, la fonction de gestionnaire ne sera exécutée que si le clic s'est produit non seulement dans le div mais dans un élément enfant correspondant au sélecteur du deuxième paramètre à .on()"span.green". De cette façon, peu importe la date de création de ces intervalles enfants, cliquer dessus exécutera toujours le gestionnaire.

Donc, pour une page qui n'ajoute pas ou ne modifie pas dynamiquement son contenu, vous ne remarquerez pas de différence entre les deux méthodes. Si vous ajoutez dynamiquement des éléments enfants supplémentaires, la deuxième syntaxe signifie que vous n'avez pas à vous soucier de leur attribuer des gestionnaires de clics car vous l'avez déjà fait une fois sur le parent.

nnnnnn
la source
5

L'explication de N3dst4 est parfaite. Sur cette base, nous pouvons supposer que tous les éléments enfants sont à l'intérieur du corps, nous n'avons donc besoin que de ceci:

$('body').on('click', '.element', function(){
    alert('It works!')
});

Cela fonctionne avec un événement direct ou délégué.

Brynner Ferreira
la source
2
jquery déconseille d'utiliser body, car il est plus lent, car le script devrait rechercher tous les enfants à l'intérieur de body, ce qui devrait être beaucoup dans la plupart des cas. Il est préférable (plus rapide) d'utiliser le conteneur parent immédiat de l'élément.
mikesoft
1
Jquery a supprimé la méthode live qui faisait la même chose que vous l'avez montré. Ceci est une performance faible et ne doit pas être utilisé.
Maciej Sikora
Dans les navigateurs modernes, la $('body').on()délégation de .elementdoit se comporter exactement de la même manière que native document.body.addEventHandler()avec un if (Event.target.className.matches(/\belement\b/))dans le rappel. Cela peut être très légèrement plus lent dans jquery en raison de la $.proxysurcharge, mais ne me citez pas là-dessus.
cowbert
2

Tangentiel à l'OP, mais le concept qui m'a aidé à démêler la confusion avec cette fonctionnalité est que les éléments liés doivent être les parents des éléments sélectionnés .

  • Bound fait référence à ce qui reste du fichier .on.
  • Sélectionné fait référence au 2ème argument de .on().

La délégation ne fonctionne pas comme .find (), en sélectionnant un sous-ensemble des éléments liés. Le sélecteur s'applique uniquement aux éléments enfants stricts.

$("span.green").on("click", ...

est très différent de

$("span").on("click", ".green", ...

En particulier, pour obtenir les avantages des conseils @ N3dst4 avec les "éléments créés à l'avenir", l'élément lié doit être un parent permanent . Ensuite, les enfants sélectionnés peuvent aller et venir.

ÉDITER

Liste de contrôle des raisons pour lesquelles la délégation .onne fonctionne pas

Raisons délicates pour lesquelles $('.bound').on('event', '.selected', some_function)peuvent ne pas fonctionner:

  1. L'élément lié n'est pas permanent . Il a été créé après avoir appelé.on()
  2. L'élément sélectionné n'est pas un enfant approprié d'un élément lié. C'est le même élément.
  3. L'élément sélectionné a empêché le bullage d'un événement vers l'élément lié en appelant .stopPropagation().

(Omettre des raisons moins délicates, comme un sélecteur mal orthographié.)

Bob Stein
la source
1

J'ai écrit un article avec une comparaison des événements directs et délégués. Je compare js pur mais cela a la même signification pour jquery qui ne fait que l'encapsuler.

La conclusion est que la gestion déléguée des événements est destinée à la structure DOM dynamique où des éléments liés peuvent être créés pendant que l'utilisateur interagit avec la page (plus besoin de liaisons), et la gestion directe des événements est destinée aux éléments DOM statiques, lorsque nous savons que la structure ne changera pas.

Pour plus d'informations et une comparaison complète - http://maciejsikora.com/standard-events-vs-event-delegation/

Utiliser toujours des gestionnaires délégués, ce qui, à mon avis, est actuellement très à la mode n'est pas la bonne manière, de nombreux programmeurs l'utilisent parce que "il devrait être utilisé", mais la vérité est que les gestionnaires d'événements directs sont meilleurs pour certaines situations et le choix de la méthode à utiliser par la connaissance des différences.

Maciej Sikora
la source
si vous attachez de nombreux gestionnaires d'événements (par exemple, chaque ligne d'une table), il est souvent préférable en termes de performances d'utiliser un seul gestionnaire délégué au conteneur au lieu d'attacher plusieurs gestionnaires directs. Vous pouvez voir cela du fait que le nombre de gestionnaires d'événements est en soi une métrique de profilage.
cowbert