jQuery rechercher des gestionnaires d'événements enregistrés avec un objet

555

J'ai besoin de savoir quels gestionnaires d'événements sont enregistrés sur un objet.

Par exemple:

$("#el").click(function() {...});
$("#el").mouseover(function() {...});

$("#el")a un clic et un survol enregistrés.

Existe-t-il une fonction pour le découvrir et éventuellement parcourir les gestionnaires d'événements?

Si ce n'est pas possible sur un objet jQuery par des méthodes appropriées, est-ce possible sur un objet DOM ordinaire?

ages04
la source
5
malheureusement, maintenant: bugs.jquery.com/ticket/10589
Skylar Saveland
2
prend en charge jQuery avant et après la version 1.8:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
2
Notez que vous pouvez utiliser les outils de développement FF et Chrome (le F12) pour voir ces écouteurs d'événements. Voir developers.google.com/web/tools/chrome-devtools/debug/… et developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/…
oriadam le

Réponses:

691

Depuis jQuery 1.8, les données d'événement ne sont plus disponibles à partir de "l'API publique" pour les données. Lisez cet article de blog jQuery . Vous devriez maintenant utiliser ceci à la place:

jQuery._data( elem, "events" );

elem doit être un élément HTML, pas un objet jQuery ou un sélecteur.

Veuillez noter qu'il s'agit d'une structure interne «privée» et ne doit pas être modifiée. Utilisez-le uniquement à des fins de débogage.

Dans les anciennes versions de jQuery, vous pourriez avoir à utiliser l'ancienne méthode qui est:

jQuery( elem ).data( "events" );
jps
la source
222
mais vous pouvez toujours utiliser $._data($(elem).get(0), "events")
bullgare
10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data («événements»): jQuery stocke ses données liées aux événements dans un objet de données nommé (attendre) des événements sur chaque élément. Il s'agit d'une structure de données interne, donc dans la version 1.8, elle sera supprimée de l'espace de nom des données utilisateur afin de ne pas entrer en conflit avec les éléments du même nom. Les données d'événement de jQuery sont toujours accessibles via jQuery._data (élément, "events") mais sachez qu'il s'agit d'une structure de données interne non documentée et qui ne doit pas être modifiée.
Sam Greenhalgh
J'ai utilisé cette méthode pour essayer de découvrir l'événement de clic d'un bouton. Dans la console Chrome, il s'affiche handler: function () {à l'intérieur de la propriété click. J'ai dû double-cliquer sur la partie fonction pour qu'elle se développe et affiche le contenu complet de la fonction.
Jim
@jim yeaaah, le double clic est la réponse
Adib Aroui
2
Prise en charge transparente des deux options:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
84

Vous pouvez le faire en explorant les événements (à partir de jQuery 1.8+), comme ceci:

$.each($._data($("#id")[0], "events"), function(i, event) {
  // i is the event type, like "click"
  $.each(event, function(j, h) {
    // h.handler is the function being called
  });
});

Voici un exemple avec lequel vous pouvez jouer:

$(function() {
  $("#el").click(function(){ alert("click"); });
  $("#el").mouseover(function(){ alert("mouseover"); });

  $.each($._data($("#el")[0], "events"), function(i, event) {
    output(i);
    $.each(event, function(j, h) {
        output("- " + h.handler);
    });
  });
});

function output(text) {
    $("#output").html(function(i, h) {
        return h + text + "<br />";
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="el">Test</div>
<code>
    <span id="output"></span>
</code>

Nick Craver
la source
2
Ceci est la bonne réponse. Le bit crucial est la seconde moitié. Cela m'a aidé à trouver un problème en moins d'une minute qui aurait pris plus d'une heure si je devais parcourir tout le code. Je vous remercie!
Andrew Ensley
2
Fonctionne avec 1.4, mais pas dans jQuery 1.8.2.
Timo Kähkönen
15
Pour jQuery 1.8+, vous devez utiliser la méthode « données privées »: au jQuery._data( jQuery("#el")[0], "events" );lieu de la méthode « données publiques »: jQuery("#el").data("events"). L' eventsobjet n'a en fait pas été stocké .data()depuis longtemps, nous avons supprimé quelques octets de code en supprimant ce "proxy" de l '"API publique"
gnarf
36

Pour jQuery 1.8+, cela ne fonctionnera plus car les données internes sont placées dans un objet différent.

La dernière façon non officielle (mais qui fonctionne également dans les versions précédentes, au moins en 1.7.2) est maintenant - $._data(element, "events")

Le trait de soulignement ("_") fait la différence ici. En interne, il appelle $.data(element, name, null, true), le dernier (quatrième) paramètre est interne ("pvt").

PhistucK
la source
$ ._ data ("body", "events") undefined $ (). jquery; "1.7.1" (essayé 1.7.2 et 1.8.1 tout le temps "indéfini")
Mars Robertson
2
@Michal - api.jquery.com/jQuery.data dit qu'il accepte un élément, pas un sélecteur.
PhistucK
1
Fonctionne maintenant correctement: $ ._ data ($ ("body"). Get (0), "events") Ou encore mieux: $ ("body"). Data ("events")!
Mars Robertson
2
FWIW - Soulignant qu'il appelle «en interne» l'autre fonction de données avec un paramètre que nous ne documentons pas spécifiquement n'est probablement pas nécessaire. Mais oui, jQuery._data( element, "events" )c'est la façon «correcte» d'obtenir ces informations maintenant.
gnarf
34

Prise sans vergogne , mais vous pouvez utiliser findHandlerJS

Pour l'utiliser, il vous suffit d'inclure findHandlersJS (ou simplement de copier-coller le code javascript brut dans la fenêtre de la console de chrome) et de spécifier le type d'événement et un sélecteur jquery pour les éléments qui vous intéressent.

Pour votre exemple, vous pouvez trouver rapidement les gestionnaires d'événements que vous avez mentionnés en faisant

findEventHandlers("click", "#el")
findEventHandlers("mouseover", "#el")

C'est ce qui est retourné:

  • élément
    L' élément réel dans lequel le gestionnaire d'événements a été enregistré
  • événements
    Tableau contenant des informations sur les gestionnaires d'événements jquery pour le type d'événement qui nous intéresse (par exemple, clic, modification, etc.)
    • gestionnaire
      Méthode de gestionnaire d'événements réels que vous pouvez voir en cliquant dessus avec le bouton droit et en sélectionnant Afficher la définition de la fonction
    • sélecteur
      Le sélecteur fourni pour les événements délégués. Il sera vide pour les événements directs.
    • cibles
      Liste avec les éléments que ce gestionnaire d'événements cible. Par exemple, pour un gestionnaire d'événements délégué qui est enregistré dans l'objet document et cible tous les boutons d'une page, cette propriété répertorie tous les boutons de la page. Vous pouvez les survoler et les voir surlignés en chrome.

Vous pouvez l'essayer ici

Rui
la source
Je pense que cela devrait être la réponse acceptée. C'est le seul qui fonctionne aussi pour moi. C'est très approfondi puisque passe par tous les éléments à la recherche d'événements.
Marquinho Peli
12

J'utilise eventbug plugin Firebug à cette fin.

Anton
la source
Merci, super astuce. L'extension ajoute un onglet à Firebug ("Événements") qui affiche les événements de la page, afin que vous puissiez les explorer facilement.
Gruber
11
De plus, les outils de développement Chrome ont des "écouteurs d'événements" sous l'onglet "Éléments" et des "points d'arrêt du récepteur d'événements" sous l'onglet "Sources".
clayzermk1
10

J'ai combiné les deux solutions de @jps à une seule fonction:

jQuery.fn.getEvents = function() {
    if (typeof(jQuery._data) === 'function') {
        return jQuery._data(this.get(0), 'events') || {};
    }

    // jQuery version < 1.7.?
    if (typeof(this.data) === 'function') {
        return this.data('events') || {};
    }

    return {};
};

Mais attention, cette fonction ne peut renvoyer que les événements définis à l'aide de jQuery lui-même.

algorhythm
la source
5

Depuis la version 1.9, il n'existe aucun moyen documenté de récupérer les événements, à part utiliser le plug-in Migrate pour restaurer l'ancien comportement. Vous pouvez utiliser la méthode _.data () comme le mentionne jps, mais il s'agit d'une méthode interne. Faites donc ce qu'il faut et utilisez le plugin Migrate si vous avez besoin de cette fonctionnalité.

Depuis la documentation jQuery sur .data("events")

Avant la version 1.9, .data ("événements") pouvait être utilisé pour récupérer la structure de données d'événement interne non documentée de jQuery pour un élément si aucun autre code n'avait défini un élément de données avec le nom "événements". Ce cas spécial a été supprimé en 1.9. Il n'y a pas d'interface publique pour récupérer cette structure de données interne et elle reste non documentée. Cependant, le plugin jQuery Migrate restaure ce comportement pour le code qui en dépend.

oligofren
la source
La réponse acceptée montre également clairement la nouvelle façon correcte de l'obtenir pour les versions récentes: jQuery._data( elem, "events" );...
Ian
2
Un moyen privé et non documenté ne sera jamais un moyen correct . La bonne façon - c'est-à-dire documentée, publique et prévue - est d'utiliser le plugin Migrate.
oligofren
3
Vous semblez mal comprendre le point du plugin Migrate. jQuery a supprimé les fonctionnalités obsolètes, et le plug-in Migrate est destiné à aider à migrer le code du développeur vers les versions les plus récentes afin qu'ils puissent immédiatement profiter des nouvelles fonctionnalités et améliorations, sans perdre de fonctionnalité. Il est destiné à aider le codeur à voir ce qu'il doit faire pour commencer correctement à utiliser les nouvelles versions de jQuery. Vous ne devez pas l'utiliser en production pour restaurer des fonctionnalités. De plus, beaucoup de choses ne sont pas documentées et à jour dans la documentation jQuery - ils l'ont déjà signalé, donc ce n'est pas une raison
Ian
De plus, s'il est inclus comme suggestion dans le blog jQuery, je l'utiliserais: blog.jquery.com/2012/08/09/jquery-1-8-released
Ian
Votre raisonnement sur le plugin Migrate semble raisonnable. OK si je supprime ma réponse?
oligofren
5

Pour rechercher des événements sur un élément:

var events = $._data(element, "events")

Notez que cela ne fonctionnera qu'avec les gestionnaires d'événements directs, si vous utilisez $ (document) .on ("nom-événement", "jq-sélecteur", fonction () {// logique}), vous voudrez voir le La fonction getEvents au bas de cette réponse

Par exemple:

 var events = $._data(document.getElementById("myElemId"), "events")

ou

 var events = $._data($("#myElemId")[0], "events")

Exemple complet:

<html>
    <head>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
        <script>
            $(function() {
                $("#textDiv").click(function() {
                    //Event Handling
                });
                var events = $._data(document.getElementById('textDiv'), "events");
                var hasEvents = (events != null);
            });
        </script>
    </head>
    <body>
        <div id="textDiv">Text</div>
    </body>
</html>

Une façon plus complète de vérifier, qui inclut des écouteurs dynamiques, installés avec $ (document) .on

function getEvents(element) {
    var elemEvents = $._data(element, "events");
    var allDocEvnts = $._data(document, "events");
    for(var evntType in allDocEvnts) {
        if(allDocEvnts.hasOwnProperty(evntType)) {
            var evts = allDocEvnts[evntType];
            for(var i = 0; i < evts.length; i++) {
                if($(element).is(evts[i].selector)) {
                    if(elemEvents == null) {
                        elemEvents = {};
                    }
                    if(!elemEvents.hasOwnProperty(evntType)) {
                        elemEvents[evntType] = [];
                    }
                    elemEvents[evntType].push(evts[i]);
                }
            }
        }
    }
    return elemEvents;
}

Exemple d'utilisation:

getEvents($('#myElemId')[0])
Tom G
la source
Cette getEventsméthode est globalement géniale, mais le fait est qu'elle donne des entrées en double dans IE11 (je sais, IE à nouveau, mais les entreprises en ont besoin ...). EDIT: $ ._ data contient des événements en double pour l'élément, même si sur FF, il ne contient aucun ... monde IE bizarre. Mais il est important de faire attention à cette possibilité d'avoir des événements en double.
Dominik Szymański
Oh, Tom, c'est en fait votre code qui multiplie les événements à chaque exécution de cette méthode. Pas bon.
Dominik Szymański
4

J'ai créé un sélecteur jQuery personnalisé qui vérifie à la fois le cache jQuery des gestionnaires d'événements affectés ainsi que les éléments qui utilisent la méthode native pour les ajouter:

(function($){

    $.find.selectors[":"].event = function(el, pos, match) {

        var search = (function(str){
            if (str.substring(0,2) === "on") {str = str.substring(2);}
            return str;
        })(String(match[3]).trim().toLowerCase());

        if (search) {
            var events = $._data(el, "events");
            return ((events && events.hasOwnProperty(search)) || el["on"+search]);
        }

        return false;

    };

})(jQuery);

Exemple:

$(":event(click)")

Cela renverra des éléments auxquels un gestionnaire de clics est attaché.

Erutan409
la source
2

Dans un navigateur moderne avec ECMAScript 5.1 / Array.prototype.map, vous pouvez également utiliser

jQuery._data(DOCUMENTELEMENT,'events')["EVENT_NAME"].map(function(elem){return elem.handler;});

dans la console de votre navigateur, qui affichera la source des gestionnaires, séparés par des virgules. Utile pour jeter un œil à tout ce qui se déroule sur un événement particulier.

Jesan Fafon
la source
jQuery._data('ct100_ContentPlaceHolder1_lcsSection','events')["EVENT_NAME"].map(function(elem){return elem.handler;}); TypeError non capturé: impossible de lire la propriété 'EVENT_NAME' de non défini à <anonyme>: 1: 62
Mike W
'ct100_ContentPlaceHolder1_lcsSection'est une chaîne, pas un élément DOM.
Jesan Fafon
2

Les événements peuvent être récupérés en utilisant:

jQuery(elem).data('events');

ou jQuery 1.8+:

jQuery._data(elem, 'events');

Remarque: Les événements liés à l'aide $('selector').live('event', handler) peuvent être récupérés à l'aide de:

jQuery(document).data('events')
R. Oosterholt
la source
2
jQuery (document) .data ('events') me donne undefined
Mike W
1

Je dois dire que la plupart des réponses sont intéressantes, mais récemment, j'ai eu un problème similaire et la solution était extrêmement simple en suivant la voie DOM. C'est différent parce que vous n'itérez pas mais visez directement l'événement dont vous avez besoin, mais ci-dessous, je donnerai une réponse plus générale.

J'avais une image d'affilée:

<table>
  <td><tr><img class="folder" /></tr><tr>...</tr></td>
</table>

Et cette image était associée à un gestionnaire d'événements Click:

imageNode.click(function () { ... });

Mon intention était d'étendre la zone cliquable à toute la ligne, j'ai donc d'abord obtenu toutes les images et les lignes relatives:

tableNode.find("img.folder").each(function () {
  var tr;

  tr = $(this).closest("tr");
  // <-- actual answer
});

Maintenant, dans la ligne de réponse réelle , je viens de faire comme suit, en donnant une réponse à la question d'origine:

tr.click(this.onclick);

J'ai donc récupéré le gestionnaire d'événements directement à partir de l'élément DOM et l'ai placé dans le gestionnaire d'événements click jQuery. Fonctionne comme un charme.

Passons maintenant au cas général. Dans les anciens jours pré-jQuery, vous pouviez obtenir tous les événements attachés à un objet avec deux fonctions simples mais puissantes offertes aux mortels par Douglas Crockford :

function walkTheDOM(node, func)
{
  func(node);
  node = node.firstChild;
  while (node)
  {
    walkTheDOM(node, func);
    node = node.nextSibling;
  }
}

function purgeEventHandlers(node)
{
  walkTheDOM(node, function (n) {
    var f;

    for (f in n)
    {
      if (typeof n[f] === "function")
      {
        n[f] = null;
      }
    }
  });
}
pid
la source
0

Une autre façon de le faire est d'utiliser simplement jQuery pour saisir l'élément, puis de parcourir le Javascript réel pour obtenir et définir et jouer avec les gestionnaires d'événements. Par exemple:

var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;
tempranova
la source
1
Je pense que jQuery fait une optimisation de la gestion des événements qui, je pense, est contournée par votre code ici.
Emile Bergeron
Merci, oui, j'avais l'impression que c'était hacky - un bon lien pour en savoir plus sur cette optimisation?
tempranova
0

J'ai combiné certaines des réponses ci-dessus et créé ce script fou mais fonctionnel qui répertorie, espérons-le, la plupart des auditeurs d'événements sur l'élément donné. N'hésitez pas à l'optimiser ici.

var element = $("#some-element");

// sample event handlers
element.on("mouseover", function () {
  alert("foo");
});

$(".parent-element").on("mousedown", "span", function () {
  alert("bar");
});

$(document).on("click", "span", function () {
  alert("xyz");
});

var collection = element.parents()
  .add(element)
  .add($(document));
collection.each(function() {
  var currentEl = $(this) ? $(this) : $(document);
  var tagName = $(this)[0].tagName ? $(this)[0].tagName : "DOCUMENT";
  var events = $._data($(this)[0], "events");
  var isItself = $(this)[0] === element[0]
  if (!events) return;
  $.each(events, function(i, event) {
    if (!event) return;
    $.each(event, function(j, h) {
      var found = false;        
      if (h.selector && h.selector.length > 0) {
        currentEl.find(h.selector).each(function () {
          if ($(this)[0] === element[0]) {
            found = true;
          }
        });
      } else if (!h.selector && isItself) {
        found = true;
      }

      if (found) {
        console.log("################ " + tagName);
        console.log("event: " + i);
        console.log("selector: '" + h.selector + "'");
        console.log(h.handler);
      }
    });
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="parent-element">
  <span id="some-element"></span>
</div>

Marek Lisý
la source
0

jQuery ne vous permet pas d'accéder simplement aux événements d'un élément donné. Vous pouvez y accéder en utilisant une méthode interne non documentée

$._data(element, "events")

Mais cela ne vous donnera toujours pas tous les événements, pour être précis ne vous montrera pas les événements assignés avec

$([selector|element]).on()

Ces événements sont stockés dans le document, vous pouvez donc les récupérer en parcourant

$._data(document, "events")

mais c'est un travail difficile, car il y a des événements pour toute la page Web.

Tom G ci-dessus a créé une fonction qui filtre le document uniquement pour les événements de l'élément donné et fusionne la sortie des deux méthodes, mais il comportait un défaut de duplication des événements dans la sortie (et en fait sur la liste d'événements internes jQuery de l'élément qui dérange votre application). J'ai corrigé cette faille et vous pouvez trouver le code ci-dessous. Collez-le simplement dans votre console de développement ou dans le code de votre application et exécutez-le si nécessaire pour obtenir une belle liste de tous les événements pour l'élément donné.

Ce qui est important à noter, l'élément est en fait HTMLElement, pas l'objet jQuery.

function getEvents(element) {
    var elemEvents = $._data(element, "events");
    var allDocEvnts = $._data(document, "events");
    function equalEvents(evt1, evt2)
    {
        return evt1.guid === evt2.guid;
    }

    for(var evntType in allDocEvnts) {
        if(allDocEvnts.hasOwnProperty(evntType)) {
            var evts = allDocEvnts[evntType];
            for(var i = 0; i < evts.length; i++) {
                if($(element).is(evts[i].selector)) {
                    if(elemEvents == null) {
                        elemEvents = {};
                    }
                    if(!elemEvents.hasOwnProperty(evntType)) {
                        elemEvents[evntType] = [];
                    }
                    if(!elemEvents[evntType].some(function(evt) { return equalEvents(evt, evts[i]); })) {
                        elemEvents[evntType].push(evts[i]);
                    }
                }
            }
        }
    }
    return elemEvents;
}
Dominik Szymański
la source