Javascript / DOM: como remover todos os eventos de um objeto DOM?

99

Apenas questione: existe alguma maneira de remover completamente todos os eventos de um objeto, por exemplo, um div?

EDIT: Estou adicionando por div.addEventListener('click',eventReturner(),false);evento.

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

EDIT2: Encontrei uma maneira que está funcionando, mas não é possível usar no meu 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
fonte
Como você anexa os eventos?
Felix Kling
2
O título pergunta sobre os elementos do objeto, enquanto a pergunta real pergunta sobre os eventos . Você deseja remover os elementos filho ou os eventos?
Pär Wieslander
oh d * mn, isso foi porque estive pensando em outra coisa quando escrevi isso ... estou me preocupando com os eventos
Florian Müller
Não sei o caso exato, mas em alguns casos, como solução alternativa, você pode usar os métodos 'on *' (como div.onclick = function), que sempre funciona com um único ouvinte e é fácil de remover como div.onclick=null. É claro que você não deve usar addEventListenerneste caso totalmente, pois isso adicionará um ouvinte separado diferente daquele em onclick.
venimus

Respostas:

108

Não tenho certeza do que você quer dizer com remover todos os eventos . Remover todos os manipuladores de um tipo específico de evento ou todos os manipuladores de um tipo?

Remova todos os manipuladores de eventos

Se você deseja remover todos os manipuladores de eventos (de qualquer tipo), pode clonar o elemento e substituí-lo por seu clone:

var clone = element.cloneNode(true);

Observação: isso preservará atributos e filhos, mas não preservará nenhuma alteração nas propriedades do DOM.


Remova manipuladores de eventos "anônimos" de tipo específico

A outra maneira é usar, removeEventListener()mas acho que você já tentou isso e não funcionou. Aqui está o problema :

Chamar addEventListeneruma função anônima cria um novo ouvinte a cada vez. Chamar removeEventListenerpara uma função anônima não tem efeito . Uma função anônima cria um objeto único cada vez que é chamada, não é uma referência a um objeto existente, embora possa chamar um. Ao adicionar um ouvinte de evento dessa maneira, certifique-se de que ele seja adicionado apenas uma vez, é permanente (não pode ser removido) até que o objeto ao qual foi adicionado seja destruído.

Você está essencialmente passando uma função anônima para addEventListeneras eventReturnerretorna uma função.

Você tem duas possibilidades para resolver isso:

  1. Não use uma função que retorna uma função. Use a função diretamente:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Crie um wrapper para addEventListenerque armazene uma referência à função retornada e crie alguma removeAllEventsfunção estranha :

    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 então você pode usá-lo com:

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

DEMO

Nota: Se o seu código for executado por um longo tempo e você estiver criando e removendo muitos elementos, você deve certificar-se de remover os elementos contidos em _eventHandlersao destruí-los.

Felix Kling
fonte
@Florian: Certamente, o código pode ser melhorado, mas deve dar uma ideia ... boa codificação!
Felix Kling
2
Você já tentou fazer isso com mais de um elemento div? o nó var será convertido em uma string, por exemplo, '[objeto HTMLDivElement]', o que significa que você acaba adicionando tudo no mesmo nó.
cstruter
@cstruter: Certo ... isso foi no começo do JavaScript ... corrigirei o código quando tiver mais tempo. Obrigado por me avisar.
Felix Kling
Acho que há um erro de digitação, addListener deveria ser addEvent
AGamePlayer
Observe que element.parentNodepode ser nulo. Provavelmente deveria colocar uma verificação if em torno do trecho de código acima.
thecoolmacdude de
46

Isso removerá todos os ouvintes das crianças, mas será lento para páginas grandes. Brutalmente simples de escrever.

element.outerHTML = element.outerHTML;
pabombs
fonte
2
Ótima resposta. Para nós de folha, essa parece ser a melhor ideia.
user3055938
3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLtrabalhou para mim.
Ryan
3
@Ryan em vez de document.querySelector('body').outerHTMLvocê pode simplesmente digitardocument.body.outerHTML
Jay Dadhania
29

Use a própria função do ouvinte de evento remove(). Por exemplo:

getEventListeners().click.forEach((e)=>{e.remove()})
JuanGG
fonte
2
Não sei por que essa não é a resposta certa. A pergunta aparece tantas vezes no SO e as respostas podem resolver o problema usando o método de clone.
jimasun
45
Parece que getEventListenerssó está disponível nas ferramentas de desenvolvimento WebKit e no FireBug. Não é algo que você pode simplesmente chamar em seu código.
corwin.amber 01 de
1
teve problemas para fazer isso funcionar, você passa um objeto como argumento para getEventListeners(). exemplo: getEventListeners(document)ougetEventListeners(document.querySelector('.someclass'))
Lucas
10
Isso parece funcionar apenas no console do Chrome . Mesmo em seu script na página, ele não funciona.
Reuel Ribeiro
4
getEventListeners(document).click.forEach((e)=>{e.remove()})produz este erro:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan
12

Como diz corwin.amber, existem diferenças entre o Webkit e os outros.

No Chrome:

getEventListeners(document);

O que dá a você um objeto com todos os ouvintes de evento existentes:

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

A partir daqui, você pode alcançar o ouvinte que deseja remover:

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

Portanto, todos os ouvintes de eventos:

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

No Firefox

É um pouco diferente porque usa um wrapper de ouvinte que não contém a função de remoção. Você deve obter o listener que deseja remover:

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

Todos os ouvintes do evento:

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

Tropecei com esta postagem tentando desativar a irritante proteção contra cópia de um site de notícias.

Apreciar!

Jmakuc
fonte
Esta é a melhor resposta para direcionar a remoção de ouvintes, como no caso de remover um ouvinte específico no nó do documento sem remover todos os seus ouvintes.
Trevor
Esta resposta está funcionando bem e melhor. @Jmakuc Muito obrigado.
Thavamani Kasi
5
O Firefox está me dizendo que getEventListenersnão está definido?
rovyko
Esta é uma solução ruim porque atualmente getEventListeners é compatível apenas com o Chrome.
Michael Paccione
2

Você pode adicionar uma função de gancho para interceptar todas as chamadas addEventHandler. O gancho empurrará o manipulador para uma lista que pode ser usada para limpeza. Por exemplo,

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

Você deve inserir este código próximo ao topo de sua página principal da web (por exemplo index.html). Durante a limpeza, você pode fazer um loop por all_handlers e chamar removeEventHandler para cada um. Não se preocupe em chamar removeEventHandler várias vezes com a mesma função. É inofensivo.

Por exemplo,

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 o IE, use Element em vez de EventTarget e mude => para funcionar, e várias outras coisas.

John Henckel
fonte
1

Para completar as respostas, aqui estão exemplos do mundo real de remoção de eventos quando você visita sites e não tem controle sobre o código HTML e JavaScript gerado.

Alguns sites irritantes estão impedindo que você copie e cole nomes de usuário em formulários de login, o que poderia ser facilmente contornado se o onpasteevento fosse adicionado com o onpaste="return false"atributo HTML. Nesse caso, basta clicar com o botão direito do mouse no campo de entrada, selecionar "Inspecionar elemento" em um navegador como o Firefox e remover o atributo HTML.

No entanto, se o evento foi adicionado por meio de JavaScript assim:

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

Teremos que remover o evento por JavaScript também:

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

No meu exemplo, usei o ID "lyca_login_mobile_no", pois era o ID de entrada de texto usado pelo site que estava visitando.

Outra forma de remover o evento (que também removerá todos os eventos) é remover o nó e criar um novo, como temos que fazer se addEventListenerfoi usado para adicionar eventos usando uma função anônima que não podemos remover com removeEventListener. Isso também pode ser feito por meio do console do navegador, inspecionando um elemento, copiando o código HTML, removendo o código HTML e, em seguida, colando o código HTML no mesmo lugar.

Também pode ser feito de forma mais rápida e automatizada por meio de JavaScript:

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

Atualização: se o aplicativo da web é feito usando uma estrutura JavaScript como Angular, parece que as soluções anteriores não estão funcionando ou quebrando o aplicativo. Outra solução alternativa para permitir a colagem seria definir o valor por meio de JavaScript:

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

No momento, não sei se existe uma maneira de remover todos os eventos de validação e restrição de formulário sem quebrar um aplicativo escrito inteiramente em JavaScript como o Angular.

baptx
fonte
1

você pode adicionar função e remover todos os outros cliques atribuindo-os

btn1 = document.querySelector(".btn-1")
btn1.addEventListener("click" , _=>{console.log("hello")})
btn1.addEventListener("click" , _=>{console.log("How Are you ?")})

btn2 = document.querySelector(".btn-2")
btn2.onclick = _=>{console.log("Hello")}
btn2.onclick = _=>{console.log("Bye")}
<button class="btn-1">Hello to Me</button>
<button class="btn-2">Hello to Bye</button>

hossein sedighian
fonte
0

angular tem um polyfill para esse problema, você pode verificar. Não entendi muito, mas talvez possa ajudar.

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
fonte
De onde EVENT_NAME_SYMBOL_REGXdeveria vir?
Elias Zamaria,
0

Você pode adicionar uma função auxiliar que limpa o ouvinte de eventos, por exemplo

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

basta passar o elemento para a função e pronto ...

Abhilash Narayan
fonte
-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

Eu não sabia qual método você está usando para anexar eventos. Pois addEventListenervocê pode usar isto:

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

mais detalhes

Ionuț Staicu
fonte
1
se eu definir assim div.onClick = something, ele define outro evento onClick, mas o anterior permanece, então eu tenho no anterior e um por exemplo nulo ...
Florian Müller
1
Não funcionará se os manipuladores forem adicionados via addEventListener().
Felix Kling
@Florian: Não, não é verdade. Se você usar esse método para adicionar um manipulador de eventos, poderá ter apenas um manipulador para um evento. Mas faz diferença se você usar addEventListener()...
Felix Kling
mas eu vi assim, então se eu adicionar a função anterior novamente, ela será chamada duas vezes ... e, como Felix King diz, isso não funciona com addEventListener ...
Florian Müller
-1

Pode ser que o navegador faça isso por você se você fizer algo como:

Copie o dive seus atributos e insira-o antes do antigo, mova o conteúdo do antigo para o novo e exclua o antigo?

Microfone
fonte
agora isso é realmente uma boa ideia, mas totalmente informante, se você tiver que fazer isso por uma quantidade de divs entre 1'000 e 1'000'000 ... Sim, é um grande projeto;)
Florian Müller
6
1 milhão de divs em uma página? Você terá outros problemas antes deste;) Pode ser usado a delegação de eventos então ...
Mic
-1

Removendo todos os eventos emdocument :

Um liner:

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

Versão bonita:

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

Apenas Chrome

Como estou tentando remover um EventListener dentro de um teste de Protractor e não tenho acesso ao Listener no teste, o seguinte funciona para remover todos os ouvintes de evento de um único tipo:

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

Espero que isso ajude alguém, pois a resposta anterior estava usando o método "remover" que desde então não funciona mais.

Filipe melo
fonte
-1

Um método é adicionar um novo ouvinte de evento que chama e.stopImmediatePropagation ().

Tomov
fonte