Javascript / DOM: ¿Cómo eliminar todos los eventos de un objeto DOM?

97

Solo pregunta: ¿Hay alguna forma de eliminar completamente todos los eventos de un objeto, por ejemplo, un div?

EDITAR: Estoy agregando por div.addEventListener('click',eventReturner(),false);evento.

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

EDIT2: Encontré una manera, que funciona, pero no es posible de usar para mi 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);
}
Florian Müller
fuente
¿Cómo adjunta los eventos?
Felix Kling
2
El título pregunta sobre los elementos del objeto, mientras que la pregunta real pregunta sobre los eventos . ¿Quieres eliminar los elementos secundarios o los eventos?
Pär Wieslander
oh maldición, eso fue porque estuve pensando en otra cosa cuando escribí eso ... me preocupan los eventos
Florian Müller
No sé el caso exacto, pero en algunos casos, como solución alternativa, podría usar los métodos 'on *' (como div.onclick = function), que siempre funciona con un solo oyente y es fácil de eliminar como div.onclick=null. Por supuesto, no debe usar addEventListeneren este caso por completo, ya que agregará un oyente diferente al que está en onclick.
venimus

Respuestas:

106

No estoy seguro de a qué te refieres con eliminar todos los eventos . ¿Eliminar todos los controladores para un tipo específico de evento o todos los controladores de eventos para un tipo?

Eliminar todos los controladores de eventos

Si desea eliminar todos los controladores de eventos (de cualquier tipo), puede clonar el elemento y reemplazarlo con su clon:

var clone = element.cloneNode(true);

Nota: Esto conservará los atributos y los hijos, pero no conservará ningún cambio en las propiedades del DOM.


Eliminar controladores de eventos "anónimos" de un tipo específico

La otra forma es usarlo, removeEventListener()pero supongo que ya lo intentaste y no funcionó. Aquí está el truco :

Llamar addEventListenera una función anónima crea un nuevo oyente cada vez. Llamar removeEventListenera una función anónima no tiene ningún efecto . Una función anónima crea un objeto único cada vez que se llama, no es una referencia a un objeto existente, aunque puede llamar a uno. Cuando agregue un detector de eventos de esta manera, asegúrese de que se agregue solo una vez, es permanente (no se puede eliminar) hasta que se destruya el objeto al que se agregó.

Básicamente, está pasando una función anónima a addEventListenercomo eventReturnerdevuelve una función.

Tienes dos posibilidades para solucionar esto:

  1. No use una función que devuelva una función. Utilice la función directamente:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Cree un contenedor para addEventListenerque almacene una referencia a la función devuelta y cree una removeAllEventsfunción extraña :

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

    Y luego podrías usarlo con:

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

MANIFESTACIÓN

Nota: Si su código se ejecuta durante mucho tiempo y está creando y eliminando muchos elementos, deberá asegurarse de eliminar los elementos contenidos en _eventHandlerscuando los destruya.

Felix Kling
fuente
@Florian: Ciertamente, el código se puede mejorar, pero debería darte una idea ... ¡feliz codificación!
Felix Kling
2
¿Has probado esto con más de un elemento div? el nodo var en realidad se convertirá en una cadena, por ejemplo, '[object HTMLDivElement]', lo que significa que terminas agregando todo al mismo nodo.
cstruter
@cstruter: Correcto ... esto fue en mis primeros días de JavaScript ... corregiré el código cuando tenga más tiempo. Gracias por hacérmelo saber.
Felix Kling
Supongo que hay un error tipográfico, addListener debería ser addEvent
AGamePlayer
Tenga en cuenta que element.parentNodepodría ser nulo. Probablemente debería poner una marca si alrededor del código anterior.
thecoolmacdude
42

Esto eliminará todos los oyentes de los niños, pero será lento para páginas grandes. Brutalmente simple de escribir.

element.outerHTML = element.outerHTML;
pabombs
fuente
2
Gran respuesta. Para los nodos hoja, esta parece ser la mejor idea.
user3055938
3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLtrabajó para mi.
Ryan
2
@Ryan en lugar de document.querySelector('body').outerHTMLusted puede simplemente escribirdocument.body.outerHTML
Jay Dadhania
28

Utilice la propia función del detector de eventos remove(). Por ejemplo:

getEventListeners().click.forEach((e)=>{e.remove()})
JuanGG
fuente
2
No estoy seguro de por qué esta no es la respuesta correcta. La pregunta aparece tantas veces en SO y puede que las respuestas manejen el problema utilizando el método de clonación.
jimasun
42
Parece que getEventListenerssolo está disponible en las herramientas de desarrollo de WebKit y en FireBug. No es algo que puedas simplemente llamar en tu código.
corwin.amber
1
tuvo problemas para hacer que esto funcione, le pasa un objeto como argumento getEventListeners(). ejemplo: getEventListeners(document)ogetEventListeners(document.querySelector('.someclass'))
Lucas
10
Esto parece funcionar solo en la consola Chrome . Incluso en su secuencia de comandos en la página no funciona.
Reuel Ribeiro
4
getEventListeners(document).click.forEach((e)=>{e.remove()})produce este error:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan
11

Como dice corwin.amber, existen diferencias entre Webkit y otros.

En Chrome:

getEventListeners(document);

Lo que le da un objeto con todos los oyentes de eventos existentes:

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

Desde aquí puede llegar al oyente que desea eliminar:

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

Entonces, todos los oyentes de eventos:

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

En Firefox

Es un poco diferente porque usa un contenedor de escucha que no contiene función de eliminación. Tienes que obtener el oyente que deseas eliminar:

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

Todos los oyentes de eventos:

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

Me encontré con esta publicación tratando de deshabilitar la molesta protección contra copias de un sitio web de noticias.

¡Disfrutar!

Jmakuc
fuente
Esta es la mejor respuesta para apuntar a la eliminación de oyentes, como en el caso de eliminar un oyente específico en el nodo del documento sin eliminar todos sus oyentes.
Trevor
Esta funciona bien y es la mejor respuesta. @Jmakuc Muchas gracias.
Thavamani Kasi
5
¿Firefox me dice que getEventListenersno está definido?
rovyko
Esta es una mala solución porque actualmente getEventListeners solo es compatible con Chrome.
Michael Paccione
1

Puede agregar una función de gancho para interceptar todas las llamadas addEventHandler. El gancho empujará al manejador a una lista que se puede usar para la limpieza. Por ejemplo,

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

Debe insertar este código cerca de la parte superior de su página web principal (por ejemplo index.html). Durante la limpieza, puede recorrer all_handlers y llamar a removeEventHandler para cada uno. No se preocupe por llamar a removeEventHandler varias veces con la misma función. Es inofensivo.

Por ejemplo,

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: para IE, use Element en lugar de EventTarget, y cambie => para funcionar, y varias otras cosas.

John Henckel
fuente
0

Para completar las respuestas, aquí hay ejemplos del mundo real de cómo eliminar eventos cuando visita sitios web y no tiene control sobre el código HTML y JavaScript generado.

Algunos sitios web molestos le impiden copiar y pegar nombres de usuario en formularios de inicio de sesión, lo que podría omitirse fácilmente si el onpasteevento se agrega con el onpaste="return false"atributo HTML. En este caso, solo necesitamos hacer clic derecho en el campo de entrada, seleccionar "Inspeccionar elemento" en un navegador como Firefox y eliminar el atributo HTML.

Sin embargo, si el evento se agregó a través de JavaScript de esta manera:

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

Tendremos que eliminar el evento a través de JavaScript también:

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

En mi ejemplo, utilicé el ID "lyca_login_mobile_no" ya que era el ID de entrada de texto utilizado por el sitio web que estaba visitando.

Otra forma de eliminar el evento (que también eliminará todos los eventos) es eliminar el nodo y crear uno nuevo, como tenemos que hacer si addEventListenerse usó para agregar eventos usando una función anónima que no podemos eliminar conremoveEventListener . Esto también se puede hacer a través de la consola del navegador inspeccionando un elemento, copiando el código HTML, eliminando el código HTML y luego pegando el código HTML en el mismo lugar.

También se puede hacer más rápido y automatizado a través de JavaScript:

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

Actualización: si la aplicación web se crea con un marco de JavaScript como Angular, parece que las soluciones anteriores no funcionan o no funcionan. Otra solución para permitir pegar sería establecer el valor a través de JavaScript:

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

Por el momento, no sé si hay una manera de eliminar todos los eventos de validación y restricción de formularios sin romper una aplicación escrita completamente en JavaScript como Angular.

baptx
fuente
0

angular tiene un polyfill para este problema, puede verificar. No entendí mucho, pero tal vez pueda ayudar.

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

....

feyzullahyildiz
fuente
¿De dónde se EVENT_NAME_SYMBOL_REGXsupone que viene?
Elias Zamaria
0

Puede agregar una función auxiliar que borre el detector de eventos, por ejemplo

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

simplemente pase el elemento a la función y eso es todo ...

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

//editar

No sabía qué método estás usando para adjuntar eventos. Porque addEventListenerpuedes usar esto:

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

mas detalles

Ionuț Staicu
fuente
1
si lo configuro así div.onClick = something, establece otro evento onClick, pero el anterior permanece, así que tengo el anterior y uno, por ejemplo, nulo ...
Florian Müller
1
No funcionará si se agregan controladores a través de addEventListener().
Felix Kling
@Florian: No, eso no es cierto. Si usa este método para agregar un controlador de eventos, solo puede tener un controlador para un evento. Pero hace una diferencia si usa addEventListener()...
Felix Kling
pero lo he visto así, así que si agrego la función anterior nuevamente, se llamará dos veces ... y, como dice Felix King, esto no funciona con addEventListener ...
Florian Müller
-1

Puede ser que el navegador lo haga por usted si hace algo como:

Copie el divy sus atributos e insértelo antes del anterior, luego mueva el contenido del antiguo al nuevo y elimine el antiguo.

Micrófono
fuente
ahora eso es en realidad una buena idea, pero totalmente deficiente, si tienes que hacer esto por una cantidad de divs entre 1'000 y 1'000'000 ... Sí, es un gran proyecto;)
Florian Müller
6
1 millón de divs en una página? Te meterás en otros problemas antes de este;) Puede usar la delegación de eventos entonces ...
Mic
-1

Eliminando todos los eventos endocument :

Un trazador de líneas:

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

Bonita versión:

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

Solo Chrome

Como intento eliminar un EventListener dentro de una prueba de transportador y no tengo acceso al Listener en la prueba, lo siguiente funciona para eliminar todos los oyentes de eventos de un solo tipo:

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

Espero que esto ayude a alguien, ya que la respuesta anterior estaba usando el método "eliminar" que desde entonces ya no funciona.

Filipe Melo
fuente
-1

Un método es agregar un nuevo detector de eventos que llame a e.stopImmediatePropagation ().

Lacho Tomov
fuente