Come verificare se esiste o meno un listener di eventi collegato dinamicamente?


105

Ecco il mio problema: è possibile in qualche modo verificare l'esistenza di un listener di eventi collegato dinamicamente? O come posso controllare lo stato della proprietà "onclick" (?) In DOM? Ho cercato una soluzione su Internet come Stack Overflow, ma senza fortuna. Ecco il mio html:

<a id="link1" onclick="linkclick(event)"> link 1 </a>
<a id="link2"> link 2 </a> <!-- without inline onclick handler -->

Quindi in Javascript allego il listener di eventi creato dinamicamente al 2 ° collegamento:

document.getElementById('link2').addEventListener('click', linkclick, false);

Il codice funziona bene, ma tutti i miei tentativi di rilevare quel listener allegato falliscono:

// test for #link2 - dynamically created eventlistener
alert(elem.onclick); // null
alert(elem.hasAttribute('onclick')); // false
alert(elem.click); // function click(){[native code]} // btw, what's this?

jsFiddle è qui . Se fai clic su "Aggiungi su clic per 2" e quindi su "[link 2]", l'evento viene attivato correttamente, ma "Test link 2" riporta sempre falso. Qualcuno può aiutare?


1
Mi dispiace dirlo, ma è impossibile ottenere le associazioni di eventi utilizzando il metodo corrente: stackoverflow.com/questions/5296858/…
Ivan

1
è possibile farlo utilizzando Chrome strumento dev: stackoverflow.com/a/41137585/863115
arufian

Puoi farlo creando il tuo spazio di archiviazione che tiene traccia degli ascoltatori. Vedi la mia risposta per ulteriori informazioni.
Angel Politis

Se l'obiettivo è impedire che un evento che è già stato aggiunto venga aggiunto di nuovo, la risposta giusta è qui . Fondamentalmente, se si utilizza una funzione denominata invece di una anonima, i listener di eventi identici duplicati verranno scartati e non è necessario preoccuparsene.
Syknapse

Se sei preoccupato per la presenza di listener duplicati, rimuovi il listener di eventi corrente prima di aggiungerlo di nuovo. Non è perfetto ma è semplice.
Jacksonkr

Risposte:


49

Non è possibile verificare se i listener di eventi associati dinamicamente esistono o meno.

L'unico modo per vedere se un listener di eventi è collegato è allegando listener di eventi in questo modo:

elem.onclick = function () { console.log (1) }

È quindi possibile verificare se un listener di eventi è stato allegato onclickrestituendo !!elem.onclick(o qualcosa di simile).


36

Ho fatto qualcosa del genere:

const element = document.getElementById('div');

if (element.getAttribute('listener') !== 'true') {
     element.addEventListener('click', function (e) {
         const elementClicked = e.target;
         elementClicked.setAttribute('listener', 'true');
         console.log('event has been attached');
    });
}

Creazione di un attributo speciale per un elemento quando il listener è collegato e quindi verifica se esiste.


5
Questo è l'approccio che ho utilizzato in passato. La mia raccomandazione sarebbe quella di utilizzare una struttura di sintassi molto specifica. IE invece di "listener", usa l'evento stesso. Quindi "dati-evento-clic". Ciò fornisce una certa flessibilità nell'evento che stai cercando di fare più di un evento e mantiene le cose un po 'più leggibili.
conrad10781

Questo con l'aggiunta di @ conrad10781 sembra il modo più affidabile e semplice. Se per qualsiasi motivo l'elemento viene rerenderd e il listener di eventi viene disconnesso, anche questo attributo verrà ripristinato.
Arye Eidelman

25

Quello che farei è creare un booleano al di fuori della funzione che inizia come FALSE e viene impostato su TRUE quando alleghi l'evento. Questo servirebbe come una sorta di bandiera per te prima di allegare nuovamente l'evento. Ecco un esempio dell'idea.

// initial load
var attached = false;

// this will only execute code once
doSomething = function()
{
 if (!attached)
 {
  attached = true;
  //code
 }
} 

//attach your function with change event
window.onload = function()
{
 var txtbox = document.getElementById('textboxID');

 if (window.addEventListener)
 {
  txtbox.addEventListener('change', doSomething, false);
 }
 else if(window.attachEvent)
 {
  txtbox.attachEvent('onchange', doSomething);
 }
}

Ciò richiede di modificare il codice dell'evento allegato per tenere traccia del fatto che sia allegato (simile a questa o questa risposta ). Non funzionerà se non controlli il codice.
user202729


6

tl; dr : No, non puoi farlo in alcun modo supportato nativamente.


L'unico modo che conosco per ottenere questo risultato sarebbe creare un oggetto di archiviazione personalizzato in cui si tiene un registro degli ascoltatori aggiunti. Qualcosa sulla seguente falsariga:

/* Create a storage object. */
var CustomEventStorage = [];

Passaggio 1: in primo luogo, avrai bisogno di una funzione che possa attraversare l'oggetto di archiviazione e restituire il record di un elemento dato l'elemento (o falso).

/* The function that finds a record in the storage by a given element. */
function findRecordByElement (element) {
    /* Iterate over every entry in the storage object. */
    for (var index = 0, length = CustomEventStorage.length; index < length; index++) {
        /* Cache the record. */
        var record = CustomEventStorage[index];

        /* Check whether the given element exists. */
        if (element == record.element) {
            /* Return the record. */
            return record;
        }
    }

    /* Return false by default. */
    return false;
}

Passaggio 2: quindi, sarà necessaria una funzione che possa aggiungere un listener di eventi ma anche inserire il listener nell'oggetto di archiviazione.

/* The function that adds an event listener, while storing it in the storage object. */
function insertListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found. */
    if (record) {
        /* Normalise the event of the listeners object, in case it doesn't exist. */
        record.listeners[event] = record.listeners[event] || [];
    }
    else {
        /* Create an object to insert into the storage object. */
        record = {
            element: element,
            listeners: {}
        };

        /* Create an array for event in the record. */
        record.listeners[event] = [];

        /* Insert the record in the storage. */
        CustomEventStorage.push(record);
    }

    /* Insert the listener to the event array. */
    record.listeners[event].push(listener);

    /* Add the event listener to the element. */
    element.addEventListener(event, listener, options);
}

Passaggio 3: per quanto riguarda il requisito effettivo della tua domanda, avrai bisogno della seguente funzione per verificare se a un elemento è stato aggiunto un listener di eventi per un evento specificato.

/* The function that checks whether an event listener is set for a given event. */
function listenerExists (element, event, listener) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether a record was found & if an event array exists for the given event. */
    if (record && event in record.listeners) {
        /* Return whether the given listener exists. */
        return !!~record.listeners[event].indexOf(listener);
    }

    /* Return false by default. */
    return false;
}

Passaggio 4: infine, avrai bisogno di una funzione che possa eliminare un listener dall'oggetto di archiviazione.

/* The function that removes a listener from a given element & its storage record. */
function removeListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found and, if found, whether the event exists. */
    if (record && event in record.listeners) {
        /* Cache the index of the listener inside the event array. */
        var index = record.listeners[event].indexOf(listener);

        /* Check whether listener is not -1. */
        if (~index) {
            /* Delete the listener from the event array. */
            record.listeners[event].splice(index, 1);
        }

        /* Check whether the event array is empty or not. */
        if (!record.listeners[event].length) {
            /* Delete the event array. */
            delete record.listeners[event];
        }
    }

    /* Add the event listener to the element. */
    element.removeEventListener(event, listener, options);
}

Frammento:


Sebbene siano trascorsi più di 5 anni da quando il PO ha pubblicato la domanda, credo che le persone che si imbatteranno in essa in futuro trarranno beneficio da questa risposta, quindi sentiti libero di dare suggerimenti o miglioramenti. 😊


Grazie per questa soluzione, è semplice e robusta. Un'osservazione però, c'è un piccolo errore. La funzione "removeListener" controlla se l'array di eventi è vuoto o meno ma il ternario non è corretto. Dovrebbe dire "if (record.listeners [evento] .length! = - 1)".
JPA

5

Puoi sempre controllare manualmente se il tuo EventListener esiste utilizzando Chrome Inspector, ad esempio. Nella scheda Elemento hai la tradizionale sottoscheda "Stili" e vicino ad essa un'altra: "Ascoltatori di eventi". Che ti darà l'elenco di tutti gli EventListeners con i loro elementi collegati.


5

Sembra strano che questo metodo non esista. È ora di aggiungerlo finalmente?

Se lo volessi, potresti qualcosa del genere:

var _addEventListener = EventTarget.prototype.addEventListener;
var _removeEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.events = {};
EventTarget.prototype.addEventListener = function(name, listener, etc) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    events[name] = [];
  }

  if (events[name].indexOf(listener) == -1) {
    events[name].push(listener);
  }

  _addEventListener(name, listener);
};
EventTarget.prototype.removeEventListener = function(name, listener) {
  var events = EventTarget.prototype.events;

  if (events[name] != null && events[name].indexOf(listener) != -1) {
    events[name].splice(events[name].indexOf(listener), 1);
  }

  _removeEventListener(name, listener);
};
EventTarget.prototype.hasEventListener = function(name) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    return false;
  }

  return events[name].length;
};

3

Ecco uno script che ho usato per verificare l'esistenza di un listener di eventi collegato dinamicamente. Ho usato jQuery per collegare un gestore di eventi a un elemento, quindi attivare quell'evento (in questo caso l'evento "click"). In questo modo posso recuperare e acquisire le proprietà degli eventi che esisteranno solo se il gestore eventi è collegato.

var eventHandlerType;

$('#contentDiv').on('click', clickEventHandler).triggerHandler('click');

function clickEventHandler(e) {
    eventHandlerType = e.type;
}

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}

2
Puoi anche assegnare una proprietà all'elemento tuo elemento fondamentalmente contrassegnandolo come elemento a cui è associato un evento;
Justin,

2

Non sembra esserci una funzione cross browser che cerca gli eventi registrati sotto un dato elemento.

Tuttavia, è possibile visualizzare le funzioni di richiamata per elementi in alcuni browser utilizzando i loro strumenti di sviluppo. Ciò può essere utile quando si tenta di determinare il funzionamento di una pagina Web o per il debug del codice.

Firefox

Per prima cosa, visualizza l'elemento nella scheda Impostazioni all'interno degli strumenti per sviluppatori. Questo può essere fatto:

  • Nella pagina facendo clic con il pulsante destro del mouse sull'elemento della pagina Web che si desidera ispezionare e selezionando "Ispeziona elemento" dal menu.
  • All'interno della console utilizzando una funzione per selezionare l'elemento, come document.querySelector , quindi facendo clic sull'icona accanto all'elemento per visualizzarlo nella scheda Impostazioni .

Se nell'elemento sono stati registrati eventi, verrà visualizzato un pulsante contenente la parola Evento accanto all'elemento. Cliccandolo ti permetterà di vedere gli eventi che sono stati registrati con l'elemento. Facendo clic sulla freccia accanto a un evento è possibile visualizzare la relativa funzione di richiamata.

Cromo

Per prima cosa, visualizza l'elemento nella scheda Elementi all'interno degli strumenti per sviluppatori. Questo può essere fatto:

  • Nella pagina facendo clic con il pulsante destro del mouse sull'elemento della pagina web che desideri esaminare e selezionando "Ispeziona" dal menu
  • All'interno della console utilizzando una funzione per selezionare l'elemento, come document.querySelector , facendo clic con il pulsante destro del mouse sull'elemento e selezionando "Rivela nel pannello Elementi" per visualizzarlo nella scheda Impostazioni .

Vicino alla sezione della finestra che mostra l'albero contenente gli elementi della pagina web, dovrebbe esserci un'altra sezione con una scheda intitolata "List Listeners". Selezionalo per vedere gli eventi che sono stati registrati nell'elemento. Per vedere il codice per un determinato evento, fare clic sul collegamento a destra di esso.

In Chrome, gli eventi per un elemento possono essere trovati anche utilizzando la funzione getEventListeners . Tuttavia, in base ai miei test, la funzione getEventListeners non elenca gli eventi quando vengono passati più elementi. Se desideri trovare tutti gli elementi nella pagina che hanno listener e visualizzare le funzioni di callback per tali listener, puoi utilizzare il seguente codice nella console per farlo:

var elems = document.querySelectorAll('*');

for (var i=0; i <= elems.length; i++) {
    var listeners = getEventListeners(elems[i]);

    if (Object.keys(listeners).length < 1) {
        continue;
    }

    console.log(elems[i]);

    for (var j in listeners) {
        console.log('Event: '+j);

        for (var k=0; k < listeners[j].length; k++) {
            console.log(listeners[j][k].listener);
        }
    }
}

Si prega di modificare questa risposta se si conoscono modi per farlo nei browser indicati o in altri browser.


1

L'ho appena scoperto cercando di vedere se il mio evento era allegato ...

se fate :

item.onclick

restituirà "null"

ma se lo fai:

item.hasOwnProperty('onclick')

allora è "TRUE"

quindi penso che quando si utilizza "addEventListener" per aggiungere gestori di eventi, l'unico modo per accedervi è tramite "hasOwnProperty". Vorrei sapere perché o come, ma purtroppo, dopo la ricerca, non ho trovato una spiegazione.


2
onclickè separato da .addEventListener- influenza un attributo mentre .addEventListenerno
Zach Saucier

1

Se ho capito bene, puoi solo controllare se un ascoltatore è stato controllato ma non quale ascoltatore è specificatamente presentatore.

Quindi un codice ad hoc riempirebbe il vuoto per gestire il flusso di codifica. Un metodo pratico sarebbe creare un stateutilizzo delle variabili. Ad esempio, collega il controllo di un ascoltatore come segue:

var listenerPresent=false

quindi se imposti un listener cambia semplicemente il valore:

listenerPresent=true

quindi all'interno del callback del tuo eventListener puoi assegnare funzionalità specifiche al suo interno e allo stesso modo, distribuire l'accesso alle funzionalità a seconda di uno stato come variabile ad esempio:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

Basta rimuovere l'evento prima di aggiungerlo:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);

Questo è un buon trucco, ma presumo che funzioni solo se hai un riferimento alla funzione che è uno degli ascoltatori di eventi attualmente "aggiunti". Penso che questo sia chiaramente un difetto dell'API standard, se possiamo aggiungere qualsiasi listener di eventi, dovrebbe anche essere possibile verificare quali listener di eventi sono stati aggiunti finora. È quasi come se gli ascoltatori di eventi attualmente fossero "variabili di sola scrittura", il che sembra un concetto oscuro.
Panu Logic

0

Ho appena scritto una sceneggiatura che ti consente di raggiungere questo obiettivo. Ti offre due funzioni globali: hasEvent(Node elm, String event)e getEvents(Node elm)che puoi utilizzare. Tieni presente che modifica il EventTargetmetodo del prototipo add/RemoveEventListenere non funziona per eventi aggiunti tramite markup HTML o sintassi javascript dielm.on_event = ...

Maggiori informazioni su GitHub

Dimostrazione dal vivo

Script:

var hasEvent,getEvents;!function(){function b(a,b,c){c?a.dataset.events+=","+b:a.dataset.events=a.dataset.events.replace(new RegExp(b),"")}function c(a,c){var d=EventTarget.prototype[a+"EventListener"];return function(a,e,f,g,h){this.dataset.events||(this.dataset.events="");var i=hasEvent(this,a);return c&&i||!c&&!i?(h&&h(),!1):(d.call(this,a,e,f),b(this,a,c),g&&g(),!0)}}hasEvent=function(a,b){var c=a.dataset.events;return c?new RegExp(b).test(c):!1},getEvents=function(a){return a.dataset.events.replace(/(^,+)|(,+$)/g,"").split(",").filter(function(a){return""!==a})},EventTarget.prototype.addEventListener=c("add",!0),EventTarget.prototype.removeEventListener=c("remove",!1)}();

-1

In teoria potresti aggiungere addEventListener e removeEventListener per aggiungere remove a flag all'oggetto "this". Brutto e non ho provato ...

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.