Javascript / DOM: come rimuovere tutti gli eventi di un oggetto DOM?


97

Domanda: esiste un modo per rimuovere completamente tutti gli eventi di un oggetto, ad esempio un div?

EDIT: sto aggiungendo per div.addEventListener('click',eventReturner(),false);un evento.

function eventReturner() {
    return function() {
        dosomething();
    };
}

EDIT2: ho trovato un modo, che funziona, ma non è possibile utilizzare per il mio caso:

var returnedFunction;
function addit() {
    var div = document.getElementById('div');
    returnedFunction = eventReturner();
    div.addEventListener('click',returnedFunction,false); //You HAVE to take here a var and not the direct call to eventReturner(), because the function address must be the same, and it would change, if the function was called again.
}
function removeit() {
    var div = document.getElementById('div');
    div.removeEventListener('click',returnedFunction,false);
}

Come alleghi gli eventi?
Felix Kling

2
Il titolo chiede degli elementi dell'oggetto, mentre la domanda vera e propria chiede degli eventi . Vuoi rimuovere gli elementi figlio o gli eventi?
Pär Wieslander

oh cazzo, era perché stavo pensando a qualcos'altro quando ho scritto che ... mi preoccupano gli eventi
Florian Müller

Non conosco il caso esatto ma in alcuni casi come soluzione alternativa potresti utilizzare i metodi "on *" (come div.onclick = funzione), che funziona sempre con un singolo ascoltatore ed è facile da rimuovere come div.onclick=null. Ovviamente non dovresti usarlo del tutto addEventListenerin questo caso poiché aggiungerà un listener separato diverso da quello in onclick.
venimus

Risposte:


106

Non sono sicuro di cosa intendi con rimuovere tutti gli eventi . Rimuovere tutti i gestori per un tipo specifico di evento o tutti i gestori di eventi per un tipo?

Rimuovi tutti i gestori di eventi

Se vuoi rimuovere tutti i gestori di eventi (di qualsiasi tipo), puoi clonare l'elemento e sostituirlo con il suo clone:

var clone = element.cloneNode(true);

Nota: questo manterrà attributi e figli, ma non manterrà le modifiche alle proprietà DOM.


Rimuovere i gestori di eventi "anonimi" di tipo specifico

L'altro modo è usare removeEventListener()ma immagino che tu abbia già provato questo e non ha funzionato. Ecco il trucco :

La chiamata addEventListenera una funzione anonima crea ogni volta un nuovo ascoltatore. La chiamata removeEventListenera una funzione anonima non ha alcun effetto . Una funzione anonima crea un oggetto univoco ogni volta che viene chiamata, non è un riferimento a un oggetto esistente sebbene possa chiamarne uno. Quando si aggiunge un listener di eventi in questo modo, assicurarsi che venga aggiunto solo una volta, è permanente (non può essere rimosso) fino a quando l'oggetto a cui è stato aggiunto non viene distrutto.

Stai essenzialmente passando una funzione anonima a addEventListenercome eventReturnerrestituisce una funzione.

Hai due possibilità per risolvere questo problema:

  1. Non utilizzare una funzione che restituisce una funzione. Usa direttamente la funzione:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Crea un wrapper per addEventListenerche memorizzi un riferimento alla funzione restituita e crea qualche removeAllEventsfunzione strana :

    var _eventHandlers = {}; // somewhere global
    
    const addListener = (node, event, handler, capture = false) => {
      if (!(event in _eventHandlers)) {
        _eventHandlers[event] = []
      }
      // here we track the events and their nodes (note that we cannot
      // use node as Object keys, as they'd get coerced into a string
      _eventHandlers[event].push({ node: node, handler: handler, capture: capture })
      node.addEventListener(event, handler, capture)
    }
    
    const removeAllListeners = (targetNode, event) => {
      // remove listeners from the matching nodes
      _eventHandlers[event]
        .filter(({ node }) => node === targetNode)
        .forEach(({ node, handler, capture }) => node.removeEventListener(event, handler, capture))
    
      // update _eventHandlers global
      _eventHandlers[event] = _eventHandlers[event].filter(
        ({ node }) => node !== targetNode,
      )
    }
    

    E poi potresti usarlo con:

    addListener(div, 'click', eventReturner(), false)
    // and later
    removeAllListeners(div, 'click')
    

DEMO

Nota: se il tuo codice viene eseguito per molto tempo e stai creando e rimuovendo molti elementi, dovresti assicurarti di rimuovere gli elementi contenuti in _eventHandlersquando li distruggi.


@Florian: Certamente, il codice può essere migliorato, ma dovrebbe darti un'idea ... felice codifica!
Felix Kling

2
Hai provato questo con più di un elemento div? la variabile del nodo verrà effettivamente convertita in una stringa, ad esempio "[object HTMLDivElement]", il che significa che si finisce per aggiungere tutto allo stesso nodo.
cstruter

@cstruter: Giusto ... questo era nei miei primi giorni di JavaScript ... correggerò il codice quando avrò più tempo. Grazie per avermi fatto sapere.
Felix Kling,

Immagino che ci sia un errore di battitura, addListener dovrebbe essere addEvent
AGamePlayer

Nota che element.parentNodepotrebbe essere nullo. Probabilmente dovrebbe mettere un controllo if attorno a quel pezzo di codice sopra.
thecoolmacdude

42

Questo rimuoverà tutti gli ascoltatori dai bambini ma sarà lento per le pagine grandi. Brutalmente semplice da scrivere.

element.outerHTML = element.outerHTML;

2
Bella risposta. Per i nodi foglia, questa sembra essere l'idea migliore.
user3055938

3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLha funzionato per me.
Ryan

2
@ Ryan invece di document.querySelector('body').outerHTMLte puoi semplicemente digitaredocument.body.outerHTML
Jay Dadhania il

28

Usa la funzione del listener di eventi remove(). Per esempio:

getEventListeners().click.forEach((e)=>{e.remove()})

2
Non sono sicuro del motivo per cui questa non è la risposta giusta. La domanda appare così tante volte su SO e le risposte potrebbero gestire il problema utilizzando il metodo clone.
jimasun

42
Sembra che getEventListenerssia disponibile solo negli strumenti di sviluppo WebKit e in FireBug. Non qualcosa che puoi semplicemente chiamare nel tuo codice.
corwin.amber

1
ha avuto problemi a farlo funzionare, hai passato un oggetto come argomento a getEventListeners(). esempio: getEventListeners(document)ogetEventListeners(document.querySelector('.someclass'))
Luca

10
Sembra funzionare solo sulla console Chrome . Anche sul tuo script a pagina non funziona.
Reuel Ribeiro

4
getEventListeners(document).click.forEach((e)=>{e.remove()})produce questo errore:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan

11

Come dice corwin.amber, ci sono differenze tra Webkit e altri.

In Chrome:

getEventListeners(document);

Che ti dà un oggetto con tutti i listener di eventi esistenti:

Object 
 click: Array[1]
 closePopups: Array[1]
 keyup: Array[1]
 mouseout: Array[1]
 mouseover: Array[1]
 ...

Da qui puoi raggiungere l'ascoltatore che desideri rimuovere:

getEventListeners(document).copy[0].remove();

Quindi tutti gli ascoltatori di eventi:

for(var eventType in getEventListeners(document)) {
   getEventListeners(document)[eventType].forEach(
      function(o) { o.remove(); }
   ) 
}

In Firefox

È leggermente diverso perché utilizza un wrapper listener che non contiene alcuna funzione di rimozione. Devi ottenere l'ascoltatore che desideri rimuovere:

document.removeEventListener("copy", getEventListeners(document).copy[0].listener)

Tutti gli ascoltatori dell'evento:

for(var eventType in getEventListeners(document)) {
  getEventListeners(document)[eventType].forEach(
    function(o) { document.removeEventListener(eventType, o.listener) }
  ) 
}

Mi sono imbattuto in questo post cercando di disabilitare la fastidiosa protezione dalla copia di un sito Web di notizie.

Godere!


Questa è la risposta migliore per mirare alla rimozione di listener, come nel caso di rimozione di un listener specifico sul nodo del documento senza rimuovere tutti i suoi listener.
Trevor

Questa risposta funziona bene e la migliore. @ Jmakuc Grazie mille.
Thavamani Kasi

5
Firefox mi sta dicendo che getEventListenersnon è definito?
rovyko

Questa è una soluzione scadente perché attualmente getEventListeners è supportato solo da Chrome.
Michele Paccione

1

È possibile aggiungere una funzione hook per intercettare tutte le chiamate addEventHandler. L'hook inserirà il gestore in un elenco che può essere utilizzato per la pulizia. Per esempio,

if (EventTarget.prototype.original_addEventListener == null) {
    EventTarget.prototype.original_addEventListener = EventTarget.prototype.addEventListener;

    function addEventListener_hook(typ, fn, opt) {
        console.log('--- add event listener',this.nodeName,typ);
        this.all_handlers = this.all_handlers || [];
        this.all_handlers.push({typ,fn,opt});
        this.original_addEventListener(typ, fn, opt);  
    }

    EventTarget.prototype.addEventListener = addEventListener_hook;
}

Dovresti inserire questo codice nella parte superiore della tua pagina web principale (ad esempio index.html). Durante la pulizia, puoi eseguire il ciclo attraverso all_handlers e chiamare removeEventHandler per ciascuno. Non preoccuparti di chiamare removeEventHandler più volte con la stessa funzione. È innocuo.

Per esempio,

function cleanup(elem) {
    for (let t in elem) if (t.startsWith('on') && elem[t] != null) {
        elem[t] = null;
        console.log('cleanup removed listener from '+elem.nodeName,t);
    } 
    for (let t of elem.all_handlers || []) {
        elem.removeEventListener(t.typ, t.fn, t.opt);
        console.log('cleanup removed listener from '+elem.nodeName,t.typ);
    } 
}

Nota: per IE usa Element invece di EventTarget e cambia => in function e varie altre cose.


0

Per completare le risposte, ecco alcuni esempi concreti di rimozione di eventi quando visiti siti Web e non hai il controllo sul codice HTML e JavaScript generato.

Alcuni siti Web fastidiosi ti impediscono di copiare e incollare i nomi utente sui moduli di accesso, che potrebbero essere facilmente aggirati se l' onpasteevento fosse aggiunto con l' onpaste="return false"attributo HTML. In questo caso è sufficiente fare clic con il pulsante destro del mouse sul campo di immissione, selezionare "Ispeziona elemento" in un browser come Firefox e rimuovere l'attributo HTML.

Tuttavia, se l'evento è stato aggiunto tramite JavaScript in questo modo:

document.getElementById("lyca_login_mobile_no").onpaste = function(){return false};

Dovremo rimuovere l'evento anche tramite JavaScript:

document.getElementById("lyca_login_mobile_no").onpaste = null;

Nel mio esempio, ho utilizzato l'ID "lyca_login_mobile_no" poiché era l'ID di immissione del testo utilizzato dal sito web che stavo visitando.

Un altro modo per rimuovere l'evento (che rimuoverà anche tutti gli eventi) è rimuovere il nodo e crearne uno nuovo, come dobbiamo fare se è addEventListenerstato utilizzato per aggiungere eventi utilizzando una funzione anonima che non possiamo rimuovere con removeEventListener. Questo può essere fatto anche tramite la console del browser ispezionando un elemento, copiando il codice HTML, rimuovendo il codice HTML e quindi incollando il codice HTML nello stesso punto.

Può anche essere fatto più velocemente e automatizzato tramite JavaScript:

var oldNode = document.getElementById("lyca_login_mobile_no");
var newNode = oldNode.cloneNode(true);
oldNode.parentNode.insertBefore(newNode, oldNode);
oldNode.parentNode.removeChild(oldNode);

Aggiornamento: se l'app Web è stata creata utilizzando un framework JavaScript come Angular, sembra che le soluzioni precedenti non funzionino o che l'app non funzionino. Un'altra soluzione alternativa per consentire l'incollaggio sarebbe impostare il valore tramite JavaScript:

document.getElementById("lyca_login_mobile_no").value = "username";

Al momento, non so se esiste un modo per rimuovere tutti gli eventi di convalida e restrizione dei moduli senza interrompere un'app scritta interamente in JavaScript come Angular.


0

angular ha un polyfill per questo problema, puoi controllare. Non ho capito molto ma forse può aiutare.

const REMOVE_ALL_LISTENERS_EVENT_LISTENER = 'removeAllListeners';

    proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function () {
        const target = this || _global;
        const eventName = arguments[0];
        if (!eventName) {
            const keys = Object.keys(target);
            for (let i = 0; i < keys.length; i++) {
                const prop = keys[i];
                const match = EVENT_NAME_SYMBOL_REGX.exec(prop);
                let evtName = match && match[1];
                // in nodejs EventEmitter, removeListener event is
                // used for monitoring the removeListener call,
                // so just keep removeListener eventListener until
                // all other eventListeners are removed
                if (evtName && evtName !== 'removeListener') {
                    this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName);
                }
            }
            // remove removeListener listener finally
            this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener');
        }
        else {
            const symbolEventNames = zoneSymbolEventNames$1[eventName];
            if (symbolEventNames) {
                const symbolEventName = symbolEventNames[FALSE_STR];
                const symbolCaptureEventName = symbolEventNames[TRUE_STR];
                const tasks = target[symbolEventName];
                const captureTasks = target[symbolCaptureEventName];
                if (tasks) {
                    const removeTasks = tasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
                if (captureTasks) {
                    const removeTasks = captureTasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
            }
        }
        if (returnTarget) {
            return this;
        }
    };

....


Da dove EVENT_NAME_SYMBOL_REGXdovrebbe venire?
Elias Zamaria il

0

Ad esempio, puoi aggiungere una funzione di supporto che cancella il listener di eventi

function clearEventListener(element) {
 const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}

basta passare l'elemento alla funzione e il gioco è fatto ...


-1
var div = getElementsByTagName('div')[0]; /* first div found; you can use getElementById for more specific element */
div.onclick = null; // OR:
div.onclick = function(){};

//modificare

Non sapevo quale metodo stai usando per allegare eventi. Perché addEventListenerpuoi usare questo:

div.removeEventListener('click',functionName,false); // functionName is the name of your callback function

maggiori dettagli


1
se lo imposto in questo div.onClick = somethingmodo imposta un altro evento su Click, ma il precedente rimane, quindi ho il precedente e uno ad esempio nullo ...
Florian Müller

1
Non funzionerà se i gestori vengono aggiunti tramite addEventListener().
Felix Kling

@Florian: No, non è vero. Se utilizzi questo metodo per aggiungere un gestore di eventi, puoi avere un solo gestore per un evento. Ma fa la differenza se usi addEventListener()...
Felix Kling

ma l'ho visto in questo modo, quindi se ho aggiunto di nuovo la funzione precedente, verrà chiamata due volte ... e, come dice Felix King, questo non funziona con addEventListener ...
Florian Müller

-1

Potrebbe essere il browser lo farà per te se fai qualcosa come:

Copia il dive i suoi attributi e inseriscilo prima del vecchio, quindi sposta il contenuto dal vecchio al nuovo ed elimina il vecchio?


ora è davvero una buona idea, ma totalmente inefficiente, se devi farlo per un ammontare di div tra 1'000 e 1'000'000 ... Sì, è un grande progetto;)
Florian Müller

6
1 milione di div in una pagina? Ti imbatterai in altri guai prima di questo;) Potrebbe essere utilizzata la delega dell'evento quindi ...
Mic

-1

Rimozione di tutti gli eventi in corso document :

Una fodera:

for (key in getEventListeners(document)) { getEventListeners(document)[key].forEach(function(c) { c.remove() }) }

Bella versione:

for (key in getEventListeners(document)) {
  getEventListeners(document)[key].forEach(function(c) {
    c.remove()
  })   
}

-1

Solo Chrome

Poiché sto cercando di rimuovere un EventListener all'interno di un test Goniometro e non ho accesso al Listener nel test, quanto segue funziona per rimuovere tutti i listener di eventi di un singolo tipo:

function(eventType){
    getEventListeners(window).[eventType].forEach(
        function(e){
            window.removeEventListener(eventType, e.listener)
        }
    );
}

Spero che questo aiuti qualcuno poiché la risposta precedente stava usando il metodo "rimuovi" che da allora non funziona più.


-1

Un metodo consiste nell'aggiungere un nuovo listener di eventi che chiama e.stopImmediatePropagation ().

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.