jQuery findet Ereignishandler, die mit einem Objekt registriert sind

555

Ich muss herausfinden, welche Ereignishandler über einem Objekt registriert sind.

Zum Beispiel:

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

$("#el")hat Click and Mouseover registriert.

Gibt es eine Funktion, um dies herauszufinden und möglicherweise die Ereignishandler zu durchlaufen?

Wenn es auf einem jQuery-Objekt mit geeigneten Methoden nicht möglich ist, ist es auf einem einfachen DOM-Objekt möglich?

Alter04
quelle
5
Leider jetzt: bugs.jquery.com/ticket/10589
Skylar Saveland
2
unterstütze sowohl jQuery vor als auch nach 1.8:var events = (jQuery._data || jQuery.data)(elem, 'events');
Oriadam
2
Beachten Sie, dass Sie die FF- und Chrome-Entwicklungstools (F12) verwenden können, um diese Ereignis-Listener anzuzeigen. Siehe developer.google.com/web/tools/chrome-devtools/debug/… und developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/…
oriadam

Antworten:

691

Ab jQuery 1.8 sind die Ereignisdaten nicht mehr über die "öffentliche API" für Daten verfügbar. Lesen Sie diesen jQuery-Blogbeitrag . Sie sollten dies jetzt stattdessen verwenden:

jQuery._data( elem, "events" );

elem sollte ein HTML-Element sein, kein jQuery-Objekt oder Selektor.

Bitte beachten Sie, dass dies eine interne, "private" Struktur ist und nicht geändert werden sollte. Verwenden Sie dies nur zum Debuggen.

In älteren Versionen von jQuery müssen Sie möglicherweise die alte Methode verwenden:

jQuery( elem ).data( "events" );
jps
quelle
222
aber Sie können immer noch verwenden $._data($(elem).get(0), "events")
Bullgare
10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data („Ereignisse“): jQuery speichert seine ereignisbezogenen Daten in einem Datenobjekt mit dem Namen (darauf warten) Ereignisse für jedes Element. Dies ist eine interne Datenstruktur, sodass diese in Version 1.8 aus dem Namensraum für Benutzerdaten entfernt wird, damit keine Konflikte mit gleichnamigen Elementen auftreten. Auf die Ereignisdaten von jQuery kann weiterhin über jQuery._data (Element, "Ereignisse") zugegriffen werden. Beachten Sie jedoch, dass dies eine interne Datenstruktur ist, die nicht dokumentiert ist und nicht geändert werden sollte.
Sam Greenhalgh
Ich habe diese Methode verwendet, um das Klickereignis einer Schaltfläche herauszufinden. In der Chrome-Konsole wurde es handler: function () {in der click-Eigenschaft angezeigt. Ich musste auf den Funktionsteil doppelklicken, um ihn zu erweitern und den vollständigen Inhalt der Funktion anzuzeigen.
Jim
@ Jim Yeaaah, der Doppelklick ist die Antwort
Adib Aroui
2
Beide Optionen var events = (jQuery._data || jQuery.data)(elem, 'events');
nahtlos
84

Sie können dies tun, indem Sie die Ereignisse (ab jQuery 1.8+) wie folgt crawlen:

$.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
  });
});

Hier ist ein Beispiel, mit dem Sie spielen können:

$(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
quelle
2
Dies ist die richtige Antwort. Das Entscheidende ist die zweite Hälfte. Dies half mir, ein Problem in weniger als einer Minute zu finden, das über eine Stunde gedauert hätte, wenn ich den gesamten Code durchsuchen müsste. Vielen Dank!
Andrew Ensley
2
Funktioniert mit 1.4, jedoch nicht in jQuery 1.8.2.
Timo Kähkönen
15
Für jQuery 1.8+, Sie müssen die ‚private Daten‘ Methode verwenden: jQuery._data( jQuery("#el")[0], "events" );statt der ‚öffentlichen Daten‘ Methode: jQuery("#el").data("events"). Das eventsObjekt wurde schon lange nicht mehr gespeichert. .data()Wir haben ein paar Bytes Code herausgeschnitten, indem wir diesen "Proxy" aus der "öffentlichen API" entfernt haben
gnarf
36

Ab jQuery 1.8+ funktioniert dies nicht mehr, da die internen Daten in einem anderen Objekt abgelegt werden.

Die neueste inoffizielle Methode (funktioniert aber auch in früheren Versionen, zumindest in Version 1.7.2) ist jetzt: $._data(element, "events")

Der Unterstrich ("_") macht hier den Unterschied. Intern wird aufgerufen $.data(element, name, null, true), der letzte (vierte) Parameter ist ein interner ("pvt").

PhistucK
quelle
$ ._ data ("body", "events") undefined $ (). jquery; "1.7.1" (versucht 1.7.2 und 1.8.1 die ganze Zeit "undefiniert")
Mars Robertson
2
@Michal - api.jquery.com/jQuery.data sagt, dass es ein Element akzeptiert, keinen Selektor.
PhistucK
1
Funktioniert jetzt einwandfrei: $ ._ data ($ ("body"). Get (0), "events") Oder noch besser: $ ("body"). Data ("events")!
Mars Robertson
2
FWIW - Es wird wahrscheinlich nicht benötigt, darauf hinzuweisen, dass die andere Datenfunktion 'intern' mit einem Parameter aufgerufen wird, den wir speziell nicht dokumentieren. Aber ja, jQuery._data( element, "events" )ist der "richtige" Weg, um diese Informationen jetzt zu erhalten.
Gnarf
34

Schamloser Stecker, aber Sie können findHandlerJS verwenden

Um es zu verwenden, müssen Sie nur findHandlersJS einschließen (oder einfach den rohen Javascript-Code kopieren und einfügen in das Konsolenfenster von Chrome ) und den Ereignistyp und eine Abfrage-Auswahl für die Elemente angeben, an denen Sie interessiert sind.

In Ihrem Beispiel könnten Sie schnell die Event-Handler finden, die Sie dabei erwähnt haben

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

Folgendes wird zurückgegeben:

  • Element
    Das tatsächliche Element, in dem der Ereignishandler registriert wurde
  • events
    Array mit Informationen zu den jquery-Ereignishandlern für den Ereignistyp, an dem wir interessiert sind (z. B. Klicken, Ändern usw.)
    • Handler
      Tatsächliche Event-Handler-Methode, die Sie sehen können, indem Sie mit der rechten Maustaste darauf klicken und Funktionsdefinition anzeigen auswählen
    • Selektor
      Der Selektor für delegierte Ereignisse. Es ist für direkte Ereignisse leer.
    • Ziele
      Liste mit den Elementen, auf die dieser Ereignishandler abzielt. Beispielsweise listet diese Eigenschaft für einen delegierten Ereignishandler, der im Dokumentobjekt registriert ist und auf alle Schaltflächen auf einer Seite abzielt, alle Schaltflächen auf der Seite auf. Sie können mit der Maus darüber fahren und sie in Chrom hervorheben.

Sie können es hier versuchen

Rui
quelle
Ich denke, das sollte die akzeptierte Antwort sein. Das ist der einzige, der auch für mich funktioniert. Es ist sehr gründlich, da alle Elemente nach Ereignissen durchsucht werden.
Marquinho Peli
12

Ich benutze Eventbug Plugin um Firebug für diesen Zweck.

Anton
quelle
Danke, toller Tipp. Die Erweiterung fügt Firebug eine Registerkarte hinzu ("Ereignisse"), auf der die Ereignisse der Seite angezeigt werden, sodass Sie sie problemlos wiederherstellen können.
Gruber
11
Außerdem verfügen die Chrome Developer Tools über "Ereignis-Listener" auf der Registerkarte "Elemente" und "Ereignis-Listener-Haltepunkte" auf der Registerkarte "Quellen".
Clayzermk1
10

Ich habe beide Lösungen von @jps zu einer Funktion kombiniert:

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 {};
};

Beachten Sie jedoch, dass diese Funktion nur Ereignisse zurückgeben kann, die mit jQuery selbst festgelegt wurden.

Algorithmus
quelle
5

Ab Version 1.9 gibt es keine dokumentierte Möglichkeit, die Ereignisse abzurufen, außer das Migrate-Plugin zu verwenden, um das alte Verhalten wiederherzustellen. Sie können die Methode _.data () als jps-Erwähnung verwenden, dies ist jedoch eine interne Methode. Tun Sie einfach das Richtige und verwenden Sie das Migrate-Plugin, wenn Sie diese Funktionalität benötigen.

Ab der jQuery-Dokumentation .data("events")

Vor 1.9 konnte .data ("Ereignisse") verwendet werden, um die undokumentierte interne Ereignisdatenstruktur von jQuery für ein Element abzurufen, wenn kein anderer Code ein Datenelement mit dem Namen "Ereignisse" definiert hatte. Dieser Sonderfall wurde in 1.9 entfernt. Es gibt keine öffentliche Schnittstelle zum Abrufen dieser internen Datenstruktur und sie bleibt undokumentiert. Das jQuery Migrate-Plugin stellt dieses Verhalten jedoch für davon abhängigen Code wieder her.

Oligofren
quelle
Die akzeptierte Antwort zeigt auch deutlich den neuen, korrekten Weg, um es für aktuelle Versionen zu bekommen: jQuery._data( elem, "events" );...
Ian
2
Ein privater, undokumentierter Weg wird niemals ein richtiger Weg sein. Der richtige Weg - dh dokumentiert, öffentlich und beabsichtigt - ist die Verwendung des Migrate-Plugins.
Oligofren
3
Sie scheinen den Sinn des Migrate-Plugins falsch zu verstehen. jQuery hat veraltete Funktionen entfernt. Das Migrate-Plugin soll dabei helfen, Entwicklercode auf die neueren Versionen zu migrieren , damit diese sofort von neuen Funktionen und Verbesserungen profitieren können, ohne jedoch die Funktionalität zu verlieren. Es soll dem Codierer helfen, zu erkennen, was er tun muss, um neue Versionen von jQuery ordnungsgemäß zu verwenden. Sie sollten es nicht in der Produktion verwenden, um Funktionen wiederherzustellen . Außerdem sind viele Dinge in der jQuery-Dokumentation nicht dokumentiert und aktuell - sie haben bereits darauf hingewiesen, das ist also kein Grund
Ian
Wenn es als Vorschlag im jQuery-Blog enthalten ist, würde ich es auch verwenden: blog.jquery.com/2012/08/09/jquery-1-8-released
Ian
Ihre Argumentation zum Migrate-Plugin erscheint vernünftig. OK, wenn ich meine Antwort lösche?
Oligofren
5

So suchen Sie nach Ereignissen für ein Element:

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

Beachten Sie, dass dies nur mit direkten Ereignishandlern funktioniert. Wenn Sie $ (document) .on ("Ereignisname", "jq-Selektor", Funktion () {// Logik}) verwenden, möchten Sie das sehen Die Funktion getEvents steht am Ende dieser Antwort

Zum Beispiel:

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

oder

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

Vollständiges Beispiel:

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

Eine umfassendere Möglichkeit zur Überprüfung, einschließlich dynamischer Listener, die mit $ (document) .on installiert wurden

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

Anwendungsbeispiel:

getEvents($('#myElemId')[0])
Tom G.
quelle
Diese getEventsMethode ist übermäßig großartig, aber die Sache ist, dass sie doppelte Einträge in IE11 liefert (ich weiß, IE wieder, aber das Unternehmen braucht es ...). BEARBEITEN: $ ._ Daten enthalten doppelte Ereignisse für Elemente, obwohl sie in FF keine ... Seltsame IE-Welt enthalten. Es ist jedoch wichtig, auf diese Möglichkeit von doppelten Ereignissen zu achten.
Dominik Szymański
Oh, Tom, es ist tatsächlich Ihr Code, der Ereignisse mit jeder Ausführung dieser Methode multipliziert. Nicht gut.
Dominik Szymański
4

Ich habe einen benutzerdefinierten jQuery-Selektor erstellt, der sowohl den Cache der zugewiesenen Ereignishandler von jQuery als auch Elemente überprüft, die die native Methode zum Hinzufügen verwenden:

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

Beispiel:

$(":event(click)")

Dadurch werden Elemente zurückgegeben, an die ein Klick-Handler angehängt ist.

Erutan409
quelle
2

In einem modernen Browser mit ECMAScript 5.1 / Array.prototype.mapkönnen Sie auch verwenden

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

In Ihrer Browserkonsole, die die Quelle der Handler druckt, wird dies durch Kommas getrennt. Nützlich, um einen Blick auf das zu werfen, was bei einem bestimmten Ereignis alles läuft.

Jesan Fafon
quelle
jQuery._data('ct100_ContentPlaceHolder1_lcsSection','events')["EVENT_NAME"].map(function(elem){return elem.handler;}); Nicht gefangener TypeError: Die Eigenschaft 'EVENT_NAME' von undefined at <anonymous> kann nicht gelesen werden: 1: 62
Mike W
'ct100_ContentPlaceHolder1_lcsSection'ist eine Zeichenfolge, kein DOM-Element.
Jesan Fafon
2

Ereignisse können abgerufen werden mit:

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

oder jQuery 1.8+:

jQuery._data(elem, 'events');

Hinweis: Ereignisse, die mit begrenzt sind, $('selector').live('event', handler) können abgerufen werden mit:

jQuery(document).data('events')
R. Oosterholt
quelle
2
jQuery (Dokument) .data ('Ereignisse') gibt mir undefiniert
Mike W
1

Ich muss sagen, dass viele der Antworten interessant sind, aber kürzlich hatte ich ein ähnliches Problem und die Lösung war extrem einfach, indem ich den DOM-Weg ging. Es ist anders, weil Sie nicht iterieren, sondern direkt auf das Ereignis zielen, das Sie benötigen, aber im Folgenden werde ich eine allgemeinere Antwort geben.

Ich hatte ein Bild in einer Reihe:

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

Und diesem Bild war ein Klickereignishandler zugeordnet:

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

Meine Absicht war es, den anklickbaren Bereich auf die gesamte Zeile zu erweitern, sodass ich zuerst alle Bilder und relativen Zeilen erhielt:

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

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

In der eigentlichen Antwortzeile habe ich nur Folgendes getan und eine Antwort auf die ursprüngliche Frage gegeben:

tr.click(this.onclick);

Also habe ich den Ereignishandler direkt aus dem DOM-Element abgerufen und in den jQuery-Click-Ereignishandler eingefügt. Klappt wunderbar.

Nun zum allgemeinen Fall. In den alten Tagen vor jQuery konnten alle Ereignisse mit zwei einfachen, aber leistungsstarken Funktionen an ein Objekt angehängt werden, die uns Sterblichen von Douglas Crockford geschenkt wurden :

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
quelle
0

Eine andere Möglichkeit besteht darin, einfach jQuery zu verwenden, um das Element zu erfassen, und dann das eigentliche Javascript zu durchlaufen, um die Ereignishandler abzurufen, festzulegen und mit ihnen zu spielen. Zum Beispiel:

var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;
Tempranova
quelle
1
Ich denke, dass jQuery eine Optimierung der Ereignisbehandlung durchführt, die meiner Meinung nach durch Ihren Code hier umgangen wird.
Emile Bergeron
Danke, ja, ich hatte das Gefühl, dass dies hackig war - gibt es einen guten Link, um mehr über diese Optimierung zu erfahren?
Tempranova
0

Ich habe einige der obigen Antworten kombiniert und dieses verrückt aussehende, aber funktionale Skript erstellt, das hoffentlich die meisten Ereignis-Listener für das angegebene Element auflistet. Fühlen Sie sich frei, es hier zu optimieren.

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ý
quelle
0

Mit jQuery können Sie nicht einfach auf die Ereignisse für ein bestimmtes Element zugreifen. Sie können mit einer nicht dokumentierten internen Methode darauf zugreifen

$._data(element, "events")

Aber es werden Ihnen immer noch nicht alle Ereignisse angezeigt, um genau zu sein, es werden Ihnen keine Ereignisse angezeigt, denen Sie zugewiesen sind

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

Diese Ereignisse werden im Dokument gespeichert, sodass Sie sie durch Durchsuchen abrufen können

$._data(document, "events")

Aber das ist harte Arbeit, da es Ereignisse für die gesamte Webseite gibt.

Tom G hat oben eine Funktion erstellt, die das Dokument nur nach Ereignissen eines bestimmten Elements filtert und die Ausgabe beider Methoden zusammenführt. Es gab jedoch einen Fehler beim Duplizieren von Ereignissen in der Ausgabe (und effektiv in der internen jQuery-Ereignisliste des Elements, die mit Ihrer Anwendung in Konflikt gerät). Ich habe diesen Fehler behoben und Sie können den Code unten finden. Fügen Sie es einfach in Ihre Entwicklungskonsole oder in Ihren App-Code ein und führen Sie es bei Bedarf aus, um eine schöne Liste aller Ereignisse für ein bestimmtes Element zu erhalten.

Was wichtig ist, ist, dass das Element tatsächlich HTMLElement ist, nicht das jQuery-Objekt.

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
quelle