jQuery znajduje procedury obsługi zdarzeń zarejestrowane dla obiektu

555

Muszę znaleźć, które procedury obsługi zdarzeń są zarejestrowane dla obiektu.

Na przykład:

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

$("#el")nie kliknąć i mouseOver zarejestrowany.

Czy istnieje funkcja, która się o tym dowie i może iteruje procedury obsługi zdarzeń?

Jeśli nie jest to możliwe na obiekcie jQuery za pomocą odpowiednich metod, czy jest to możliwe na zwykłym obiekcie DOM?

wieku04
źródło
5
niestety teraz: bugs.jquery.com/ticket/10589
Skylar Saveland
2
obsługuje zarówno jQuery przed, jak i po 1.8:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
2
Pamiętaj, że możesz użyć narzędzi programistycznych FF i Chrome (F12), aby zobaczyć te detektory zdarzeń. Zobacz developers.google.com/web/tools/chrome-devtools/debug/… i developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/…
oriadam

Odpowiedzi:

691

Począwszy od jQuery 1.8, dane zdarzenia nie są już dostępne z „publicznego interfejsu API” dla danych. Przeczytaj ten post na blogu jQuery . Powinieneś teraz użyć tego zamiast:

jQuery._data( elem, "events" );

elem powinien być elementem HTML, a nie obiektem jQuery lub selektorem.

Pamiętaj, że jest to wewnętrzna, „prywatna” struktura i nie powinna być modyfikowana. Używaj tego tylko do celów debugowania.

W starszych wersjach jQuery może być konieczne użycie starej metody:

jQuery( elem ).data( "events" );
jps
źródło
222
ale nadal możesz używać $._data($(elem).get(0), "events")
bullgare
10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data („zdarzenia”): jQuery przechowuje dane związane ze zdarzeniem w obiekcie danych o nazwie (czekaj na to) zdarzenia w każdym elemencie. Jest to wewnętrzna struktura danych, więc w wersji 1.8 zostanie ona usunięta z przestrzeni nazw danych użytkownika, aby nie powodowała konfliktu z elementami o tej samej nazwie. Do danych zdarzeń jQuery nadal można uzyskać dostęp za pośrednictwem jQuery._data (element, „zdarzenia”), ale należy pamiętać, że jest to wewnętrzna struktura danych, która jest nieudokumentowana i nie powinna być modyfikowana.
Sam Greenhalgh,
Korzystałem z tej metody, aby znaleźć zdarzenie kliknięcia przycisku. W konsoli Chrome wyświetlał się handler: function () {we właściwości click. Musiałem dwukrotnie kliknąć część funkcji, aby ją rozwinąć i wyświetlić pełną zawartość funkcji.
Jim
@jim yeaaah, podwójne kliknięcie jest odpowiedzią
Adib Aroui
2
Bezproblemowo obsługują obie opcje:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
84

Możesz to zrobić, indeksując zdarzenia (od jQuery 1.8+), w następujący sposób:

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

Oto przykład, z którym możesz grać:

$(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
źródło
2
To jest poprawna odpowiedź. Kluczowym elementem jest druga połowa. Pomogło mi to znaleźć problem w niecałą minutę, który zająłby ponad godzinę, gdybym musiał przeszukać cały kod. Dziękuję Ci!
Andrew Ensley,
2
Działa z 1.4, ale nie w jQuery 1.8.2.
Timo Kähkönen,
15
Dla jQuery 1.8+, to należy użyć metody „prywatne dane”: jQuery._data( jQuery("#el")[0], "events" );zamiast metody „dane publiczne” jQuery("#el").data("events"). eventsObiekt nie został faktycznie przechowywane w .data()przez długi czas, my przycięte kilka bajtów kodu przez usunięcie tej „proxy” z „publicznych API”
Gnarf
36

W przypadku jQuery 1.8+ nie będzie to już działać, ponieważ dane wewnętrzne są umieszczane w innym obiekcie.

Najnowszym nieoficjalnym (ale działa również w poprzednich wersjach, przynajmniej w wersji 1.7.2) sposobem jest teraz - $._data(element, "events")

Podkreśla („_”), co robi różnicę tutaj. Wewnętrznie się woła $.data(element, name, null, true), ostatni (czwarty) parametr to wewnętrzny („pvt”).

PhistucK
źródło
$ ._ data („body”, „events”) undefined $ (). jquery; „1.7.1” (wypróbowane przez cały czas 1.7.2 i 1.8.1 „niezdefiniowane”)
Mars Robertson
2
@Michal - api.jquery.com/jQuery.data mówi, że akceptuje element, a nie selektor.
PhistucK
1
Teraz działa dobrze: $ ._ data ($ („body”). Get (0), „events”) Lub jeszcze lepiej: $ („body”). Data („events”)!
Mars Robertson,
2
FWIW - Wskazanie, że „wewnętrznie” wywołuje inną funkcję danych z parametrem, którego konkretnie nie dokumentujemy, prawdopodobnie nie jest potrzebne. Ale tak, jQuery._data( element, "events" )jest teraz „prawidłowym” sposobem na uzyskanie tych informacji.
gnarf
34

Bezwstydna wtyczka, ale możesz użyć findHandlerJS

Aby z niego skorzystać, wystarczy dołączyć findHandlersJS (lub po prostu skopiować i wkleić surowy kod javascript do okna konsoli chrome) oraz określić typ zdarzenia i selektor jquery dla elementów, którymi jesteś zainteresowany.

Na przykład możesz szybko znaleźć programy obsługi zdarzeń, o których wspomniałeś, robiąc

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

Oto, co zostanie zwrócone:

  • element
    Rzeczywisty element, w którym zarejestrowano moduł obsługi zdarzeń
  • events
    Tablica z informacjami o modułach obsługi zdarzeń jquery dla interesującego nas typu zdarzenia (np. kliknięcie, zmiana itp.)
    • handler
      Rzeczywista metoda obsługi zdarzeń, którą można zobaczyć, klikając ją prawym przyciskiem myszy i wybierając opcję Pokaż definicję funkcji
    • selektor
      Selektor przewidziany dla zdarzeń delegowanych. Będzie pusty dla bezpośrednich wydarzeń.
    • target
      Wymień elementy zawierające elementy, na które cel ten obsługuje. Na przykład dla procedury obsługi zdarzeń delegowanych, która jest zarejestrowana w obiekcie dokumentu i jest kierowana do wszystkich przycisków na stronie, ta właściwość wyświetli wszystkie przyciski na stronie. Możesz je najechać myszką i zobaczyć je podświetlone w chromie.

Możesz spróbować tutaj

Rui
źródło
Myślę, że powinna to być zaakceptowana odpowiedź. To jedyny, który działa również dla mnie. Jest to bardzo dokładne, ponieważ przechodzi przez wszystkie elementy w poszukiwaniu zdarzeń.
Marquinho Peli,
12

Używam eventbug wtyczki do Firebug do tego celu.

Anton
źródło
Dzięki, świetna wskazówka. Rozszerzenie dodaje kartę Firebug („Wydarzenia”), która pokazuje zdarzenia na stronie, dzięki czemu można je łatwo wyeksportować.
Gruber
11
Ponadto Chrome Developer Tools ma „Detektory zdarzeń” na karcie „Elementy” i „Punkty przerwania detektora zdarzeń” na karcie „Źródła”.
clayzermk1
10

Połączyłem oba rozwiązania od @jps do jednej funkcji:

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

Ale uwaga, ta funkcja może zwracać tylko zdarzenia ustawione za pomocą samego jQuery.

Algorytm
źródło
5

Od wersji 1.9 nie ma udokumentowanego sposobu odzyskiwania zdarzeń poza użyciem wtyczki Migrate w celu przywrócenia starego zachowania. Możesz użyć metody _.data (), jak wspomina jps, ale jest to metoda wewnętrzna. Więc po prostu postępuj właściwie i skorzystaj z wtyczki Migrate, jeśli potrzebujesz tej funkcji.

Z dokumentacji jQuery na .data("events")

Przed wersją 1.9 .data („zdarzenia”) można było wykorzystać do odzyskania nieudokumentowanej wewnętrznej struktury danych zdarzeń jQuery dla elementu, jeśli żaden inny kod nie zdefiniował elementu danych o nazwie „zdarzenia”. Ten specjalny przypadek został usunięty w wersji 1.9. Nie ma publicznego interfejsu do pobierania tej wewnętrznej struktury danych i pozostaje ona nieudokumentowana. Jednak wtyczka jQuery Migrate przywraca to zachowanie dla zależnego od niej kodu.

oligofren
źródło
Przyjęta odpowiedź wyraźnie pokazuje nowy, poprawny sposób uzyskania jej dla najnowszych wersji: jQuery._data( elem, "events" );...
Ian
2
Prywatny, nieudokumentowany sposób nigdy nie będzie poprawny . Prawidłowym sposobem - czyli udokumentowanym, publicznym i zamierzonym - jest użycie wtyczki Migrate.
oligofren
3
Wygląda na to, że źle zrozumiałeś punkt wtyczki Migrate. jQuery usunął przestarzałe funkcje, a wtyczka Migrate pomaga migrować kod programisty do nowszych wersji, dzięki czemu mogą one od razu korzystać z nowych funkcji i ulepszeń, ale nie tracić funkcjonalności. Ma to na celu pomóc programistom zobaczyć, co muszą zrobić, aby poprawnie zacząć korzystać z nowych wersji jQuery. Nie należy używać go w środowisku produkcyjnym do przywracania funkcji. Ponadto wiele rzeczy nie jest udokumentowanych i aktualnych w dokumentacji jQuery - już o tym wspominali, więc to nie jest powód
Ian
Ponadto, jeśli jest uwzględniony jako sugestia w blogu jQuery, użyłbym
Ian
Twoje uzasadnienie dotyczące wtyczki Migrate wydaje się rozsądne. OK, jeśli usunę moją odpowiedź?
oligofren
5

Aby sprawdzić zdarzenia w elemencie:

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

Zauważ, że będzie to działać tylko z bezpośrednimi modułami obsługi zdarzeń, jeśli używasz $ (document) .on („nazwa-zdarzenia”, „selektor jq”, funkcja () {// logika}), będziesz chciał zobaczyć Funkcja getEvents na dole tej odpowiedzi

Na przykład:

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

lub

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

Pełny przykład:

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

Bardziej kompletny sposób sprawdzenia, obejmujący dynamiczne nasłuchiwania, zainstalowany z $ (dokument) .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;
}

Przykładowe użycie:

getEvents($('#myElemId')[0])
Tom G.
źródło
Ta getEventsmetoda jest zbyt świetna, ale chodzi o to, że daje zduplikowane wpisy w IE11 (znowu wiem, IE, ale firma tego potrzebuje ...). EDYCJA: Dane $ ._ zawierają zduplikowane zdarzenia dla elementu, chociaż na FF nie zawiera żadnego ... Dziwnego świata IE. Ale ważne jest, aby uważać na możliwość posiadania zduplikowanych wydarzeń.
Dominik Szymański
Och, Tom, to właściwie twój kod zwielokrotnia zdarzenia z każdym wykonaniem tej metody. Niedobrze.
Dominik Szymański
4

Utworzyłem niestandardowy selektor jQuery, który sprawdza zarówno pamięć podręczną przypisanych procedur obsługi zdarzeń jQuery, jak i elementy, które używają natywnej metody ich dodawania:

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

Przykład:

$(":event(click)")

Zwróci to elementy, do których dołączony jest moduł obsługi kliknięć.

Erutan409
źródło
2

W nowoczesnej przeglądarce z ECMAScript 5.1 / Array.prototype.mapmożesz także używać

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

w konsoli przeglądarki, która wydrukuje źródło programów obsługi, rozdzielane przecinkami. Przydatne do zerknięcia na to, co dzieje się na danym wydarzeniu.

Jesan Fafon
źródło
jQuery._data('ct100_ContentPlaceHolder1_lcsSection','events')["EVENT_NAME"].map(function(elem){return elem.handler;}); Uncaught TypeError: Nie można odczytać właściwości „EVENT_NAME” z niezdefiniowanej w <anonymous>: 1: 62
Mike W
'ct100_ContentPlaceHolder1_lcsSection'jest ciągiem, a nie elementem DOM.
Jesan Fafon
2

Zdarzenia można odzyskać za pomocą:

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

lub jQuery 1.8+:

jQuery._data(elem, 'events');

Uwaga: Zdarzenia ograniczone przy użyciu $('selector').live('event', handler) można odzyskać za pomocą:

jQuery(document).data('events')
R. Oosterholt
źródło
2
jQuery (dokument) .data („wydarzenia”) daje mi niezdefiniowany
Mike W
1

Muszę powiedzieć, że wiele odpowiedzi jest interesujących, ale ostatnio miałem podobny problem i rozwiązanie było niezwykle proste, idąc drogą DOM. Jest inaczej, ponieważ nie iterujesz, ale celujesz bezpośrednio w potrzebne wydarzenie, ale poniżej podam bardziej ogólną odpowiedź.

Miałem zdjęcie z rzędu:

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

Do tego obrazu dołączony jest moduł obsługi zdarzenia kliknięcia:

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

Moim zamiarem było rozszerzenie obszaru klikalnego na cały wiersz, więc najpierw otrzymałem wszystkie obrazy i wiersze względne:

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

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

Teraz w rzeczywistej anwer linii Właśnie zrobił następująco, dając odpowiedź na oryginalne pytanie:

tr.click(this.onclick);

Więc pobrałem moduł obsługi zdarzeń bezpośrednio z elementu DOM i umieściłem go w module obsługi zdarzeń kliknięcia jQuery. Działa jak marzenie.

Teraz do ogólnego przypadku. W dawnych czasach przed jQuery wszystkie zdarzenia były dołączane do obiektu za pomocą dwóch prostych, ale potężnych funkcji, które dali nam śmiertelnicy 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
źródło
0

Innym sposobem na to jest użycie jQuery do przechwycenia elementu, a następnie przejrzenie faktycznego Javascript, aby uzyskać, ustawić i grać z modułami obsługi zdarzeń. Na przykład:

var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;
tempranova
źródło
1
Myślę, że jQuery wykonuje optymalizację obsługi zdarzeń, które moim zdaniem są omijane przez twój kod tutaj.
Emile Bergeron,
Dzięki, tak, miałem wrażenie, że to był hacky - dobry link, aby dowiedzieć się więcej o tej optymalizacji?
tempranova
0

Połączyłem niektóre z powyższych odpowiedzi i stworzyłem ten szalenie wyglądający, ale funkcjonalny skrypt, który, mam nadzieję, wymienia większość słuchaczy zdarzeń w danym elemencie. Tutaj możesz go zoptymalizować.

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ý
źródło
0

jQuery nie pozwala ci po prostu uzyskać dostępu do zdarzeń dla danego elementu. Możesz uzyskać do nich dostęp za pomocą nieudokumentowanej metody wewnętrznej

$._data(element, "events")

Ale wciąż nie da ci wszystkich wydarzeń, a ściślej mówiąc, nie pokaże ci wydarzeń, do których przypisano

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

Te zdarzenia są przechowywane w dokumencie, dzięki czemu można je pobrać, przeglądając

$._data(document, "events")

ale to ciężka praca, ponieważ są wydarzenia dla całej strony.

Tom G powyżej stworzył funkcję, która filtruje dokument tylko pod kątem zdarzeń danego elementu i łączy dane wyjściowe obu metod, ale miał wadę powielania zdarzeń w danych wyjściowych (i skutecznie na wewnętrznej liście zdarzeń jQuery elementu bałaganu w aplikacji). Naprawiłem tę wadę, a kod znajdziesz poniżej. Wystarczy wkleić go do konsoli programisty lub kodu aplikacji i wykonać w razie potrzeby, aby uzyskać ładną listę wszystkich zdarzeń dla danego elementu.

Należy zauważyć, że element jest tak naprawdę HTMLElement, a nie obiektem 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
źródło