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?



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

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>


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


$ ._ 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


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.


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.


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.


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

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ęć.


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.


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

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


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;

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>


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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.