Come trovare i listener di eventi su un nodo DOM durante il debug o dal codice JavaScript?


841

Ho una pagina in cui alcuni listener di eventi sono allegati alle caselle di input e alle caselle di selezione. C'è un modo per scoprire quali listener di eventi stanno osservando un particolare nodo DOM e per quale evento?

Gli eventi vengono allegati utilizzando:

  1. Prototipo Event.observe ;
  2. DOM's addEventListener;
  3. Come attributo elemento element.onclick.

3
Come sono collegati gli eventi in primo luogo? Stai utilizzando una libreria (ad esempio Prototype, jQuery, ecc.)?
Crescent Fresh,

È importante notare che è possibile collegare più funzioni di callback per lo stesso tipo di evento tramite element.addEventListener(type, callback, [bubble]), mentre element.onclick = functionverranno sovrascritte ogni volta che si assegna.
Braden Best

Risposte:


507

Se hai solo bisogno di ispezionare ciò che sta accadendo su una pagina, potresti provare il bookmarklet Visual Event .

Aggiornamento : Visual Event 2 disponibile.


1
Perfetto! Il EventBugplugin di Beats Firebug .
Robin Maben,

22
Questo aggiunge un milione di elementi alla pagina, molti dei quali sono immagini. La sua utilità è notevolmente ridotta su una pagina con molti gestori di eventi (il mio ha 17k e Visual Event ha impiegato circa 3 minuti per caricare).
Tony R

15
Credo che l'estensione di Chrome @ssharma menzionata sia quella disponibile qui
kmote

1
Visial Event non funziona se la pagina fa riferimento a una libreria js di terze parti. Viene generato un errore: XMLHttpRequest non può caricare A.com/js/jquery-ui-1.10.3.custom.js?_=1384831682813 . Origin B.com non è consentito da Access-Control-Allow-Origin.
superstrada

5
Gli strumenti per sviluppatori interni di Chrome e FF (F12) hanno un visualizzatore di eventi integrato! Chrome: nella scheda "Elementi", seleziona ed elemento e vedi a destra ci sono: Ascoltatori di
oriadam

365

Dipende da come sono collegati gli eventi. Per presumere l'illustrazione abbiamo il seguente gestore di clic:

var handler = function() { alert('clicked!') };

Lo collegheremo al nostro elemento usando metodi diversi, alcuni che consentono l'ispezione e altri che non lo fanno.

Metodo A) gestore di eventi singoli

element.onclick = handler;
// inspect
alert(element.onclick); // alerts "function() { alert('clicked!') }"

Metodo B) gestori di eventi multipli

if(element.addEventListener) { // DOM standard
    element.addEventListener('click', handler, false)
} else if(element.attachEvent) { // IE
    element.attachEvent('onclick', handler)
}
// cannot inspect element to find handlers

Metodo C): jQuery

$(element).click(handler);
  • 1.3.x

    // inspect
    var clickEvents = $(element).data("events").click;
    jQuery.each(clickEvents, function(key, value) {
        alert(value) // alerts "function() { alert('clicked!') }"
    })
  • 1.4.x (memorizza il gestore all'interno di un oggetto)

    // inspect
    var clickEvents = $(element).data("events").click;
    jQuery.each(clickEvents, function(key, handlerObj) {
        alert(handlerObj.handler) // alerts "function() { alert('clicked!') }"
        // also available: handlerObj.type, handlerObj.namespace
    })

(Vedi jQuery.fn.datae jQuery.data)

Metodo D): prototipo (disordinato)

$(element).observe('click', handler);
  • 1.5.x

    // inspect
    Event.observers.each(function(item) {
        if(item[0] == element) {
            alert(item[2]) // alerts "function() { alert('clicked!') }"
        }
    })
  • Da 1.6 a 1.6.0.3, incluso (qui è diventato molto difficile)

    // inspect. "_eventId" is for < 1.6.0.3 while 
    // "_prototypeEventID" was introduced in 1.6.0.3
    var clickEvents = Event.cache[element._eventId || (element._prototypeEventID || [])[0]].click;
    clickEvents.each(function(wrapper){
        alert(wrapper.handler) // alerts "function() { alert('clicked!') }"
    })
  • 1.6.1 (un po 'meglio)

    // inspect
    var clickEvents = element.getStorage().get('prototype_event_registry').get('click');
    clickEvents.each(function(wrapper){
        alert(wrapper.handler) // alerts "function() { alert('clicked!') }"
    })

3
Grazie per averlo aggiornato. È un peccato che tu debba ripetere ogni tipo di gestore.
Keith Bentrup,

4
Su "Metodo B" (addEventListener) ecco una risposta riguardante lo stato delle strutture di enumerazione per i gestori registrati con API eventi DOM puri: stackoverflow.com/questions/7810534/…
Nickolay,

@Giovanni: grazie per il commento. Hai un esempio di "JavaScript reale" per recuperare i listener aggiunti in precedenza tramite addEventListener?
Crescent Fresh,

6
@Jan, questo non sembra funzionare per jQuery 1.7.2, esiste un approccio diverso per quella versione?
tomdemuyt,

14
@tomdemuyt In jQuery, gli eventi sono ora archiviati in un array di dati interno anziché essere accessibili .data('events')come prima. Per accedere ai dati interni dell'evento, utilizzare $._data(elem, 'events'). Si noti che questa funzione è contrassegnata "solo per uso interno" nella fonte jQuery, quindi nessuna promessa che funzionerà sempre in futuro, ma credo che abbia funzionato da quando jQuery 1.7 e funziona ancora.
Matt Browne,

351

Supporto di Chrome, Firefox, Vivaldi e Safari getEventListeners(domElement)nella loro console degli strumenti di sviluppo.

Per la maggior parte degli scopi di debug, questo potrebbe essere usato.

Di seguito è riportato un ottimo riferimento per usarlo: https://developers.google.com/web/tools/chrome-devtools/console/utilities#geteventlisteners


1
+1. Il codice specifico del browser non è un problema per me, perché sto cercando di far funzionare correttamente una pagina che non controllo.
Grault,

9
bello, ma funziona solo nella riga di comando della console di Chrome :(
gillyspy il

5
@Raghav ah grazie, l'utilizzo NON è : come pensavo inizialmente :)getEventListeners(object) obj getEventListeners()
jave.web

11
Avresti potuto risparmiarmi 3 ore se avessi spiegato come ottenere il codice sorgente di un listener di eventi. Quel "riferimento" non ha spiegato nulla. Nel caso in cui nessun altro era in una perdita ecco come: var list = getEventListeners(document.getElementById("YOURIDHERE")); console.log(list["focus"][0]["listener"].toString()). Cambia "focus" sull'evento che vuoi controllare e il numero se ci sono più listener nello stesso evento.
doABarrelRoll721,

46
SUGGERIMENTO: getEventListeners($0)otterrà i listener di eventi per l'elemento su cui ti sei concentrato negli strumenti di sviluppo di Chrome. Lo uso sempre. Adoro non dover usare le funzioni querySelector o get__By_
cacoder

91

Adesso WebKit Inspector nei browser Chrome o Safari lo fa. Visualizzerà i listener di eventi per un elemento DOM quando lo selezioni nel riquadro Elementi.


9
Non sono sicuro che mostra tutti i gestori di eventi; solo il singolo gestore di eventi HTML.
huyz,

3
Dovrei menzionare il plug-in EventBug per Firebug per completezza < softwareishard.com/blog/category/eventbug >
Nickolay,

Questo ha reso la mia giornata, un codice prototipo legacy aveva più funzioni legate allo stesso evento sullo stesso elemento e stavo morendo cercando di rintracciarle. Grazie mille Ishan.
siliconrockstar,

70

È possibile elencare tutti i listener di eventi in JavaScript: non è così difficile; devi solo hackerare il prototypemetodo degli elementi HTML ( prima di aggiungere i listener).

function reportIn(e){
    var a = this.lastListenerInfo[this.lastListenerInfo.length-1];
    console.log(a)
}


HTMLAnchorElement.prototype.realAddEventListener = HTMLAnchorElement.prototype.addEventListener;

HTMLAnchorElement.prototype.addEventListener = function(a,b,c){
    this.realAddEventListener(a,reportIn,c); 
    this.realAddEventListener(a,b,c); 
    if(!this.lastListenerInfo){  this.lastListenerInfo = new Array()};
    this.lastListenerInfo.push({a : a, b : b , c : c});
};

Ora ogni elemento anchor ( a) avrà una lastListenerInfoproprietà che contiene tutti i suoi listener. E funziona anche per rimuovere ascoltatori con funzioni anonime.


4
Questo metodo non funzionerà se stai scrivendo uno script utente o uno script di contenuto. In questi giorni è probabile che non solo vengano sottoposti a sandbox, ma come si può garantire l'ordine di esecuzione?
huyz,

questo metodo funziona con chrome / 19 e FF / 12. l'ordine di esecuzione può essere garantito se lo agganci prima di altri script
Tzury Bar Yochay,

8
Non potresti semplicemente modificare Node.prototypeinvece? Ecco dove HTMLAnchorElementeredita .addEventListenercomunque. '
Esailija,

3
Stai presupponendo che l'agente utente implementa l'ereditarietà del prototipo per gli oggetti host DOM e ti consente di modificarli. Nessuna di queste è una buona idea: non modificare oggetti che non possiedi .
RobG,

1
Questo è piuttosto inutile: mostra quali ListList sono stati aggiunti, il che può essere buono in alcuni casi, ma non ciò che è stato rimosso. Quindi, se stai cercando di eseguire il debug di ciò che è stato rimosso e ciò che non lo era, semplicemente non c'è modo.
gotofritz,

52

Usa getEventListeners in Google Chrome :

getEventListeners(document.getElementByID('btnlogin'));
getEventListeners($('#btnlogin'));

1
Uncaught ReferenceError: getEventListeners non è definito
Bryan Grace,

3
getEventListeners funziona solo nella console di devtools di Chrome. E questo è un duplicato di questa risposta più elaborata: stackoverflow.com/a/16544813/1026
Nickolay,

41

(Riscrivere la risposta da questa domanda poiché è pertinente qui.)

Durante il debug, se vuoi solo vedere gli eventi, ti consiglio di ...

  1. Evento visivo
  2. La sezione Elementi degli Strumenti per sviluppatori di Chrome: seleziona un elemento e cerca "Ascoltatori di eventi" in basso a destra (simile a Firefox)

Se si desidera utilizzare gli eventi nel proprio codice e si sta utilizzando jQuery prima della versione 1.8 , è possibile utilizzare:

$(selector).data("events")

per ottenere gli eventi. A partire dalla versione 1.8, l'utilizzo di .data ("eventi") è sospeso (vedere questo ticket bug ). Puoi usare:

$._data(element, "events")

Un altro esempio: scrivere tutti gli eventi clic su un determinato collegamento nella console:

var $myLink = $('a.myClass');
console.log($._data($myLink[0], "events").click);

(vedi http://jsfiddle.net/HmsQC/ per un esempio funzionante)

Sfortunatamente, usando $ ._ dati questo non è raccomandato tranne che per il debug poiché è una struttura jQuery interna e potrebbe cambiare nelle versioni future. Purtroppo non conosco altri mezzi facili per accedere agli eventi.


1
$._data(elem, "events")non sembra funzionare con jQuery 1.10, almeno per gli eventi registrati utilizzando $(elem).on('event', ...). Qualcuno sa come eseguire il debug di quelli?
Marius Gedminas,

4
Non sono positivo, ma penso che $._datapotrebbe essere necessario che il primo parametro sia un elemento e non un oggetto jQuery. Quindi ho bisogno di correggere il mio esempio sopra per essereconsole.log($._data($myLink[0], "events").click);
Luca

29

1: Prototype.observeusa Element.addEventListener (vedi il codice sorgente )

2: è possibile eseguire Element.addEventListenerl' override per ricordare i listener aggiunti (la pratica proprietà è EventListenerListstata rimossa dalla proposta di specifica DOM3). Esegui questo codice prima di allegare qualsiasi evento:

(function() {
  Element.prototype._addEventListener = Element.prototype.addEventListener;
  Element.prototype.addEventListener = function(a,b,c) {
    this._addEventListener(a,b,c);
    if(!this.eventListenerList) this.eventListenerList = {};
    if(!this.eventListenerList[a]) this.eventListenerList[a] = [];
    this.eventListenerList[a].push(b);
  };
})();

Leggi tutti gli eventi di:

var clicks = someElement.eventListenerList.click;
if(clicks) clicks.forEach(function(f) {
  alert("I listen to this function: "+f.toString());
});

E non dimenticare di eseguire l'override Element.removeEventListenerper rimuovere l'evento dall'abitudine Element.eventListenerList.

3: la Element.onclickproprietà ha bisogno di cure speciali qui:

if(someElement.onclick)
  alert("I also listen tho this: "+someElement.onclick.toString());

4: non dimenticare l' Element.onclickattributo content: queste sono due cose diverse:

someElement.onclick = someHandler; // IDL attribute
someElement.setAttribute("onclick","otherHandler(event)"); // content attribute

Quindi devi gestirlo anche tu:

var click = someElement.getAttribute("onclick");
if(click) alert("I even listen to this: "+click);

Il bookmarklet di Visual Event (menzionato nella risposta più popolare) ruba solo la cache del gestore librerie personalizzata:

Si scopre che non esiste un metodo standard fornito dall'interfaccia DOM consigliata dal W3C per scoprire quali listener di eventi sono collegati a un particolare elemento. Sebbene ciò possa sembrare una svista, c'era una proposta per includere una proprietà chiamata eventListenerList nella specifica DOM di livello 3, ma purtroppo è stata rimossa nelle bozze successive. Pertanto, siamo costretti a esaminare le singole librerie Javascript, che in genere mantengono una cache di eventi collegati (in modo che possano essere successivamente rimossi ed eseguire altre utili astrazioni).

Pertanto, affinché Visual Event possa mostrare gli eventi, deve essere in grado di analizzare le informazioni sull'evento da una libreria Javascript.

L'override degli elementi può essere discutibile (ovvero perché ci sono alcune funzionalità specifiche del DOM come le raccolte live, che non possono essere codificate in JS), ma fornisce il supporto di EventListenerList in modo nativo e funziona in Chrome, Firefox e Opera (non funziona in IE7 ).


23

Puoi racchiudere i metodi DOM nativi per la gestione dei listener di eventi ponendo questo in cima a <head>:

<script>
    (function(w){
        var originalAdd = w.addEventListener;
        w.addEventListener = function(){
            // add your own stuff here to debug
            return originalAdd.apply(this, arguments);
        };

        var originalRemove = w.removeEventListener;
        w.removeEventListener = function(){
            // add your own stuff here to debug
            return originalRemove.apply(this, arguments);
        };
    })(window);
</script>

H / T @ les2


Per favore, correggimi se sbaglio, ma non è solo per i listener di eventi collegati alla finestra?
Maciej Krawczyk,

@MaciejKrawczyk sì, e quelli che bolle
Jon z

18

Gli strumenti per sviluppatori di Firefox ora fanno questo. Gli eventi vengono mostrati facendo clic sul pulsante "ev" sulla destra del display di ciascun elemento, inclusi gli eventi jQuery e DOM .

Schermata del pulsante del listener di eventi degli strumenti di sviluppo di Firefox nella scheda Impostazioni


Ho guardato uno degli elementi dom nella pagina a cui il bookmarklet di Visual Event 2 mostra un evento collegato a, e ho visto il piccolo pulsante "ev" sulla destra, come tu mostri.
Eric

Il listener di eventi di Firefox potrebbe scoprire gli eventi delegati di jQuery mentre Chrome ha bisogno del plugin per farlo.
Nick Tsai,


10

Soluzione completamente funzionante basata sulla risposta di Jan Turon - si comporta come getEventListeners()da console:

(C'è un piccolo bug con i duplicati. Comunque non si rompe molto.)

(function() {
  Element.prototype._addEventListener = Element.prototype.addEventListener;
  Element.prototype.addEventListener = function(a,b,c) {
    if(c==undefined)
      c=false;
    this._addEventListener(a,b,c);
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(!this.eventListenerList[a])
      this.eventListenerList[a] = [];
    //this.removeEventListener(a,b,c); // TODO - handle duplicates..
    this.eventListenerList[a].push({listener:b,useCapture:c});
  };

  Element.prototype.getEventListeners = function(a){
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(a==undefined)
      return this.eventListenerList;
    return this.eventListenerList[a];
  };
  Element.prototype.clearEventListeners = function(a){
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(a==undefined){
      for(var x in (this.getEventListeners())) this.clearEventListeners(x);
        return;
    }
    var el = this.getEventListeners(a);
    if(el==undefined)
      return;
    for(var i = el.length - 1; i >= 0; --i) {
      var ev = el[i];
      this.removeEventListener(a, ev.listener, ev.useCapture);
    }
  };

  Element.prototype._removeEventListener = Element.prototype.removeEventListener;
  Element.prototype.removeEventListener = function(a,b,c) {
    if(c==undefined)
      c=false;
    this._removeEventListener(a,b,c);
      if(!this.eventListenerList)
        this.eventListenerList = {};
      if(!this.eventListenerList[a])
        this.eventListenerList[a] = [];

      // Find the event in the list
      for(var i=0;i<this.eventListenerList[a].length;i++){
          if(this.eventListenerList[a][i].listener==b, this.eventListenerList[a][i].useCapture==c){ // Hmm..
              this.eventListenerList[a].splice(i, 1);
              break;
          }
      }
    if(this.eventListenerList[a].length==0)
      delete this.eventListenerList[a];
  };
})();

Uso:

someElement.getEventListeners([name]) - restituisce l'elenco dei listener di eventi, se il nome è impostato restituisce la matrice di listener per tale evento

someElement.clearEventListeners([name]) - rimuove tutti i listener di eventi, se è impostato il nome rimuovi solo i listener per tale evento


3
Questa è una bella risposta, ma non riesco a farlo funzionare sugli elementi bodye document.
David Bradshaw,

Soluzione davvero bella!
Dzhakhar Ukhaev,

@Peter Mortensen, perché hai cambiato il nome di Jan? :)
n00b

1
@David Bradshaw: perché corpo, documento e finestra hanno il loro prototipo e non il prototipo Element ...
Stefan Steiger,

1
@ n00b: c'è un bug. Ecco perché hmmm. Si utilizza l'operatore virgola nell'istruzione if per listener e useCaption ... L'operatore virgola valuta ciascuno dei suoi operandi (da sinistra a destra) e restituisce il valore dell'ultimo operando. Quindi rimuovi il primo eventListener che corrisponde a useCapture, indipendentemente dal tipo di evento ... Dovrebbe essere && anziché virgola.
Stefan Steiger,

8

Opera 12 (non l'ultimo motore basato su Chrome Webkit) Dragonfly ha avuto questo per un po 'di tempo ed è ovviamente visualizzato nella struttura DOM. A mio avviso, è un debugger superiore ed è l'unica ragione rimasta per cui uso ancora la versione basata su Opera 12 (non esiste la versione v13, v14 e la base v15 Webkit manca ancora di Dragonfly)

inserisci qui la descrizione dell'immagine


4

Prototipo 1.7.1 via

function get_element_registry(element) {
    var cache = Event.cache;
    if(element === window) return 0;
    if(typeof element._prototypeUID === 'undefined') {
        element._prototypeUID = Element.Storage.UID++;
    }
    var uid =  element._prototypeUID;           
    if(!cache[uid]) cache[uid] = {element: element};
    return cache[uid];
}

4

Di recente ho lavorato con eventi e volevo visualizzare / controllare tutti gli eventi in una pagina. Dopo aver esaminato le possibili soluzioni, ho deciso di seguire la mia strada e creare un sistema personalizzato per monitorare gli eventi. Quindi, ho fatto tre cose.

Innanzitutto, avevo bisogno di un contenitore per tutti i listener di eventi nella pagina: questo è l' EventListenersoggetto. Ha tre metodi utili: add(), remove(), e get().

Successivamente, ho creato un EventListeneroggetto per contenere le informazioni necessarie per l'evento, cioè: target, type, callback, options, useCapture, wantsUntrusted, e aggiunto un metodo remove()per rimuovere l'ascoltatore.

Infine, ho esteso il metodo nativo addEventListener()e i removeEventListener()metodi per farli funzionare con gli oggetti che ho creato ( EventListenere EventListeners).

Uso:

var bodyClickEvent = document.body.addEventListener("click", function () {
    console.log("body click");
});

// bodyClickEvent.remove();

addEventListener()crea un EventListeneroggetto, lo aggiunge EventListenerse restituisce l' EventListeneroggetto, in modo che possa essere rimosso in seguito.

EventListeners.get()può essere utilizzato per visualizzare gli ascoltatori nella pagina. Accetta una EventTargeto una stringa (tipo di evento).

// EventListeners.get(document.body);
// EventListeners.get("click");

dimostrazione

Diciamo che vogliamo conoscere tutti gli ascoltatori di eventi in questa pagina corrente. Possiamo farlo (supponendo che tu stia utilizzando un'estensione del gestore di script, Tampermonkey in questo caso). Il seguente script fa questo:

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @include      https://stackoverflow.com/*
// @grant        none
// ==/UserScript==

(function() {
    fetch("https://raw.githubusercontent.com/akinuri/js-lib/master/EventListener.js")
        .then(function (response) {
            return response.text();
        })
        .then(function (text) {
            eval(text);
            window.EventListeners = EventListeners;
        });
})(window);

E quando elenchiamo tutti gli ascoltatori, dice che ci sono 299 ascoltatori di eventi. "Sembra" che ci siano dei duplicati, ma non so se siano davvero duplicati. Non tutti i tipi di eventi sono duplicati, quindi tutti quei "duplicati" potrebbero essere un singolo ascoltatore.

screenshot della console che elenca tutti i listener di eventi in questa pagina

Il codice può essere trovato nel mio repository. Non volevo pubblicarlo qui perché è piuttosto lungo.


Aggiornamento: questo non sembra funzionare con jQuery. Quando esamino EventListener, vedo che il callback è

function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}

Credo che questo appartenga a jQuery e non sia il vero callback. jQuery memorizza il callback effettivo nelle proprietà di EventTarget:

$(document.body).click(function () {
    console.log("jquery click");
});

inserisci qui la descrizione dell'immagine

Per rimuovere un listener di eventi, il callback effettivo deve essere passato al removeEventListener()metodo. Quindi, per farlo funzionare con jQuery, ha bisogno di ulteriori modifiche. Potrei risolverlo in futuro.


Funziona alla grande! Grazie
mt025,

3

Sto provando a farlo in jQuery 2.1 e con il $().click() -> $(element).data("events").click;metodo " " non funziona.

Mi sono reso conto che nel mio caso funzionano solo le funzioni $ ._ data ():

	$(document).ready(function(){

		var node = $('body');
		
        // Bind 3 events to body click
		node.click(function(e) { alert('hello');  })
			.click(function(e) { alert('bye');  })
			.click(fun_1);

        // Inspect the events of body
		var events = $._data(node[0], "events").click;
		var ev1 = events[0].handler // -> function(e) { alert('hello')
		var ev2 = events[1].handler // -> function(e) { alert('bye')
		var ev3 = events[2].handler // -> function fun_1()
        
		$('body')
			.append('<p> Event1 = ' + eval(ev1).toString() + '</p>')
			.append('<p> Event2 = ' + eval(ev2).toString() + '</p>')
			.append('<p> Event3 = ' + eval(ev3).toString() + '</p>');        
	
	});

	function fun_1() {
		var txt = 'text del missatge';	 
		alert(txt);
	}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
</body>



1

la modifica di queste funzioni ti consentirà di registrare i listener aggiunti:

EventTarget.prototype.addEventListener
EventTarget.prototype.attachEvent
EventTarget.prototype.removeEventListener
EventTarget.prototype.detachEvent

leggi il resto degli ascoltatori con

console.log(someElement.onclick);
console.log(someElement.getAttribute("onclick"));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.