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.