Javascript / DOM: Comment supprimer tous les événements d'un objet DOM?

97

Juste question: existe-t-il un moyen de supprimer complètement tous les événements d'un objet, par exemple un div?

EDIT: J'ajoute par div.addEventListener('click',eventReturner(),false);événement.

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

EDIT2: j'ai trouvé un moyen, qui fonctionne, mais pas possible d'utiliser pour mon cas:

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
la source
Comment attachez-vous les événements?
Felix Kling
2
Le titre pose des questions sur les éléments de l'objet, tandis que la question réelle porte sur les événements . Voulez-vous supprimer les éléments enfants ou les événements?
Pär Wieslander
oh d * mn, c'est parce que j'ai pensé à autre chose quand j'ai écrit ça ... je me soucie des événements
Florian Müller
Je ne connais pas le cas exact, mais dans certains cas, vous pouvez utiliser les méthodes 'on *' (comme div.onclick = function), qui fonctionnent toujours avec un seul écouteur et sont faciles à supprimer en tant que div.onclick=null. Bien sûr, vous ne devriez pas utiliser addEventListenercomplètement dans ce cas car cela ajoutera un auditeur distinct différent de celui dans onclick.
venimus

Réponses:

106

Je ne suis pas sûr de ce que vous entendez par supprimer tous les événements . Supprimer tous les gestionnaires pour un type spécifique d'événement ou tous les gestionnaires d'événements pour un type?

Supprimer tous les gestionnaires d'événements

Si vous souhaitez supprimer tous les gestionnaires d'événements (de tout type), vous pouvez cloner l'élément et le remplacer par son clone:

var clone = element.cloneNode(true);

Remarque: Cela conservera les attributs et les enfants, mais ne conservera aucune modification des propriétés DOM.


Supprimer les gestionnaires d'événements "anonymes" d'un type spécifique

L'autre façon est d'utiliser removeEventListener()mais je suppose que vous avez déjà essayé cela et cela n'a pas fonctionné. Voici le hic :

L'appel addEventListenerà une fonction anonyme crée un nouvel auditeur à chaque fois. L'appel removeEventListenerà une fonction anonyme n'a aucun effet . Une fonction anonyme crée un objet unique à chaque fois qu'elle est appelée, ce n'est pas une référence à un objet existant bien qu'elle puisse en appeler un. Lors de l'ajout d'un écouteur d'événements de cette manière, assurez-vous qu'il n'est ajouté qu'une seule fois, il est permanent (ne peut pas être supprimé) jusqu'à ce que l'objet auquel il a été ajouté soit détruit.

Vous passez essentiellement une fonction anonyme en addEventListenertant que eventReturnerretourne une fonction.

Vous avez deux possibilités pour résoudre ce problème:

  1. N'utilisez pas une fonction qui renvoie une fonction. Utilisez la fonction directement:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Créez un wrapper pour addEventListenerqui stocke une référence à la fonction retournée et créez une removeAllEventsfonction étrange :

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

    Et puis vous pouvez l'utiliser avec:

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

DEMO

Remarque: Si votre code s'exécute pendant une longue période et que vous créez et supprimez de nombreux éléments, vous devez vous assurer de supprimer les éléments contenus dans _eventHandlerslorsque vous les détruisez.

Félix Kling
la source
@Florian: Certes, le code peut être amélioré, mais cela devrait vous donner une idée ... bon codage!
Felix Kling
2
Avez-vous essayé cela avec plus d'un élément div? le nœud var sera en fait converti en une chaîne, par exemple «[object HTMLDivElement]», ce qui signifie que vous finissez par tout ajouter au même nœud.
cstruter le
@cstruter: D'accord ... c'était à mes débuts de JavaScript ... Je corrigerai le code quand je trouverai un peu plus de temps. Merci de me le faire savoir.
Felix Kling
Je suppose qu'il y a une faute de frappe, addListener devrait être addEvent
AGamePlayer
Notez que cela element.parentNodepourrait être nul. Devrait probablement mettre une vérification if autour de ce morceau de code ci-dessus.
thecoolmacdude
42

Cela supprimera tous les auditeurs des enfants mais sera lent pour les grandes pages. Brutalement simple à écrire.

element.outerHTML = element.outerHTML;
pabombes
la source
2
Très bonne réponse. Pour les nœuds feuilles, cela semble être la meilleure idée.
user3055938
3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTMLtravaillé pour moi.
Ryan
2
@Ryan au lieu de document.querySelector('body').outerHTMLvous pouvez simplement taperdocument.body.outerHTML
Jay Dadhania
28

Utilisez la propre fonction de l'écouteur d'événements remove(). Par exemple:

getEventListeners().click.forEach((e)=>{e.remove()})
JuanGG
la source
2
Je ne sais pas pourquoi ce n'est pas la bonne réponse. La question apparaît tant de fois sur SO et les réponses peuvent gérer le problème en utilisant la méthode de clonage.
jimasun
42
Il semble que ce getEventListenersne soit disponible que dans les outils de développement WebKit et dans FireBug. Pas quelque chose que vous pouvez simplement appeler dans votre code.
corwin.amber
1
eu du mal à faire fonctionner cela, vous passez un objet comme argument à getEventListeners(). exemple: getEventListeners(document)ougetEventListeners(document.querySelector('.someclass'))
Luke
10
Cela semble ne fonctionner que sur la console Chrome . Même sur votre script à la page, cela ne fonctionne pas.
Reuel Ribeiro
4
getEventListeners(document).click.forEach((e)=>{e.remove()})produit cette erreur:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan
11

Comme le dit corwin.amber, il existe des différences entre Webkit et d'autres.

Dans Chrome:

getEventListeners(document);

Ce qui vous donne un objet avec tous les écouteurs d'événements existants:

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

De là, vous pouvez atteindre l'auditeur que vous souhaitez supprimer:

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

Donc, tous les écouteurs d'événements:

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

Dans Firefox

Est un peu différent car il utilise un wrapper d'écoute qui ne contient aucune fonction de suppression. Vous devez obtenir l'auditeur que vous souhaitez supprimer:

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

Tous les écouteurs d'événements:

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

Je suis tombé sur ce post en essayant de désactiver la protection contre la copie ennuyeuse d'un site Web d'actualités.

Prendre plaisir!

Jmakuc
la source
C'est la meilleure réponse pour cibler la suppression des écouteurs, par exemple en cas de suppression d'un écouteur spécifique sur le nœud de document sans supprimer tous ses écouteurs.
Trevor
Cela fonctionne bien et la meilleure réponse. @Jmakuc Merci beaucoup.
Thavamani Kasi
5
Firefox me dit que ce getEventListenersn'est pas défini?
rovyko
C'est une solution médiocre car actuellement getEventListeners n'est pris en charge que par Chrome.
Michael Paccione
1

Vous pouvez ajouter une fonction de crochet pour intercepter tous les appels addEventHandler. Le hook poussera le gestionnaire vers une liste qui peut être utilisée pour le nettoyage. Par exemple,

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

Vous devez insérer ce code en haut de votre page Web principale (par exemple index.html). Pendant le nettoyage, vous pouvez parcourir all_handlers et appeler removeEventHandler pour chacun. Ne vous inquiétez pas d'appeler plusieurs fois removeEventHandler avec la même fonction. C'est inoffensif.

Par exemple,

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

Remarque: pour IE, utilisez Element au lieu d'EventTarget, et changez => en fonction, et diverses autres choses.

John Henckel
la source
0

Pour compléter les réponses, voici des exemples concrets de suppression d'événements lorsque vous visitez des sites Web et que vous n'avez pas de contrôle sur le code HTML et JavaScript généré.

Certains sites Web ennuyeux vous empêchent de copier-coller les noms d'utilisateur sur les formulaires de connexion, ce qui pourrait facilement être contourné si l' onpasteévénement était ajouté avec l' onpaste="return false"attribut HTML. Dans ce cas, il suffit de faire un clic droit sur le champ de saisie, de sélectionner «Inspecter l'élément» dans un navigateur comme Firefox et de supprimer l'attribut HTML.

Cependant, si l'événement a été ajouté via JavaScript comme ceci:

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

Nous devrons également supprimer l'événement via JavaScript:

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

Dans mon exemple, j'ai utilisé l'ID "lyca_login_mobile_no" car il s'agissait de l'ID d'entrée de texte utilisé par le site Web que je visitais.

Une autre façon de supprimer l'événement (qui supprimera également tous les événements) est de supprimer le nœud et d'en créer un nouveau, comme nous devons le faire si a addEventListenerété utilisé pour ajouter des événements à l'aide d'une fonction anonyme que nous ne pouvons pas supprimer avec removeEventListener. Cela peut également être fait via la console du navigateur en inspectant un élément, en copiant le code HTML, en supprimant le code HTML, puis en collant le code HTML au même endroit.

Cela peut également être fait plus rapidement et automatisé via JavaScript:

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

Mise à jour: si l'application Web est créée à l'aide d'un framework JavaScript comme Angular, il semble que les solutions précédentes ne fonctionnent pas ou ne cassent pas l'application. Une autre solution de contournement pour autoriser le collage consiste à définir la valeur via JavaScript:

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

Pour le moment, je ne sais pas s'il existe un moyen de supprimer tous les événements de validation et de restriction de formulaire sans casser une application entièrement écrite en JavaScript comme Angular.

baptx
la source
0

angular a un polyfill pour ce problème, vous pouvez vérifier. Je n'ai pas beaucoup compris mais peut-être que cela peut aider.

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
la source
D'où est EVENT_NAME_SYMBOL_REGXcensé venir?
Elias Zamaria le
0

Vous pouvez ajouter une fonction d'assistance qui efface l'écouteur d'événements par exemple

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

il suffit de passer l'élément à la fonction et c'est tout ...

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

//Éditer

Je ne savais pas quelle méthode utilisez-vous pour attacher des événements. Car addEventListenervous pouvez utiliser ceci:

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

plus de détails

Ionuț Staicu
la source
1
si je le définis comme div.onClick = somethingalors il définit un autre événement onClick, mais le précédent reste, donc j'ai le précédent et un par exemple nul ...
Florian Müller
1
Ne fonctionnera pas si les gestionnaires sont ajoutés via addEventListener().
Felix Kling
@Florian: Non, ce n'est pas vrai. Si vous utilisez cette méthode pour ajouter un gestionnaire d'événements, vous ne pouvez avoir qu'un seul gestionnaire pour un événement. Mais cela fait une différence si vous utilisez addEventListener()...
Felix Kling
mais je l'ai vu comme ça, donc si j'ajoute à nouveau la fonction précédente, elle sera appelée deux fois ... et, comme le dit Felix King, cela ne fonctionne pas avec addEventListener ...
Florian Müller
-1

Peut-être que le navigateur le fera pour vous si vous faites quelque chose comme:

Copiez le divet ses attributs et insérez-le avant l'ancien, puis déplacez le contenu de l'ancien vers le nouveau et supprimez l'ancien?

Micro
la source
maintenant c'est en fait une bonne idée, mais totalement inperformante, si vous devez faire cela pour une quantité de divs entre 1'000 et 1'000'000 ... Oui, c'est un gros projet;)
Florian Müller
6
1M divs dans une page? Vous aurez d'autres problèmes avant celui-ci;) Peut-être utiliser la délégation d'événements alors ...
Mic
-1

Suppression de tous les événements sur document :

Bon mot:

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

Jolie version:

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

Chrome uniquement

Comme j'essaie de supprimer un EventListener dans un test Protractor et que je n'ai pas accès à l'écouteur dans le test, ce qui suit fonctionne pour supprimer tous les écouteurs d'événements d'un seul type:

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

J'espère que cela aide quelqu'un car la réponse précédente utilisait la méthode "supprimer" qui depuis lors ne fonctionne plus.

Filipe Melo
la source
-1

Une méthode consiste à ajouter un nouvel écouteur d'événements qui appelle e.stopImmediatePropagation ().

Lacho Tomov
la source