Comment vérifier si l'écouteur d'événements attaché dynamiquement existe ou non?

103

Voici mon problème: est-il possible d'une manière ou d'une autre de vérifier l'existence d'un écouteur d'événements attaché dynamiquement? Ou comment puis-je vérifier l'état de la propriété "onclick" (?) Dans DOM? J'ai cherché une solution sur Internet, comme Stack Overflow, mais pas de chance. Voici mon html:

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

Puis en Javascript, j'attache un écouteur d'événement créé dynamiquement au 2ème lien:

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

Le code fonctionne bien, mais toutes mes tentatives pour détecter cet écouteur attaché échouent:

// 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 se trouve ici . Si vous cliquez sur "Ajouter un clic pour 2" puis sur "[lien 2]", l'événement se déclenche bien, mais le "Test lien 2" rapporte toujours faux. Quelqu'un peut-il aider?

Stano
la source
1
Je suis désolé de le dire, mais il est impossible d'obtenir les liaisons d'événements en utilisant votre méthode actuelle: stackoverflow.com/questions/5296858/…
Ivan
1
vous pouvez le faire en utilisant l'outil de développement chrome: stackoverflow.com/a/41137585/863115
arufian
Vous pouvez le faire en créant votre propre stockage qui garde la trace des auditeurs. Voir ma réponse pour plus.
Angel Politis
Si l'objectif est d'empêcher l'ajout d'un événement déjà ajouté, la bonne réponse est ici . Fondamentalement, si vous utilisez une fonction nommée au lieu d'une fonction anonyme, les écouteurs d'événements identiques en double seront supprimés et vous n'avez pas à vous en soucier.
Syknapse
Si vous craignez d'avoir des écouteurs en double, supprimez l'écouteur d'événement actuel avant de l'ajouter à nouveau. Ce n'est pas parfait mais c'est simple.
Jacksonkr

Réponses:

49

Il n'existe aucun moyen de vérifier si des écouteurs d'événements attachés dynamiquement existent ou non.

La seule façon de voir si un écouteur d'événement est attaché est de joindre des écouteurs d'événement comme ceci:

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

Vous pouvez ensuite tester si un écouteur d'événement a été attaché onclicken retournant !!elem.onclick(ou quelque chose de similaire).

Ivan
la source
33

J'ai fait quelque chose comme ça:

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

Création d'un attribut spécial pour un élément lorsque l'auditeur est attaché, puis vérification de son existence.

Karol Grabowski
la source
4
C'est l'approche que j'ai utilisée dans le passé. Ma recommandation serait d'utiliser une structure de syntaxe très spécifique. IE au lieu de "listener", utilisez l'événement lui-même. Donc "data-event-click". Cela offre une certaine flexibilité dans le cas où vous cherchez à faire plus d'un événement et garde les choses un peu plus lisibles.
conrad10781
Ceci avec l'ajout de @ conrad10781 semble être le moyen le plus fiable et le plus simple. Si, pour une raison quelconque, l'élément est renvoyé et que l'écouteur d'événements est déconnecté, cet attribut sera également réinitialisé.
Arye Eidelman
23

Ce que je ferais, c'est créer un booléen en dehors de votre fonction qui commence par FALSE et est défini sur TRUE lorsque vous attachez l'événement. Cela vous servirait d'une sorte de drapeau avant de rattacher à nouveau l'événement. Voici un exemple de l'idée.

// 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);
 }
}
César
la source
Cela vous oblige à changer le code de l'événement attaché pour savoir s'il est attaché (similaire à telle ou telle réponse ). Cela ne fonctionnera pas si vous ne contrôlez pas le code.
user202729
6

tl; dr : Non, vous ne pouvez pas faire cela de manière native.


Le seul moyen que je connaisse pour y parvenir serait de créer un objet de stockage personnalisé dans lequel vous conservez un enregistrement des écouteurs ajoutés. Quelque chose dans le sens suivant:

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

Étape 1: Tout d'abord, vous aurez besoin d'une fonction qui peut traverser l'objet de stockage et retourner l'enregistrement d'un élément donné l'élément (ou false).

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

Étape 2: Ensuite, vous aurez besoin d'une fonction qui peut ajouter un écouteur d'événements, mais également insérer l'écouteur dans l'objet de stockage.

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

Étape 3: En ce qui concerne l'exigence réelle de votre question, vous aurez besoin de la fonction suivante pour vérifier si un élément a été ajouté à un écouteur d'événement pour un événement spécifié.

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

Étape 4: Enfin, vous aurez besoin d'une fonction permettant de supprimer un écouteur de l'objet de stockage.

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

Fragment:


Bien que plus de 5 ans se soient écoulés depuis que le PO a publié la question, je crois que les gens qui tomberont dessus à l'avenir bénéficieront de cette réponse, alors n'hésitez pas à y faire des suggestions ou des améliorations. 😊

Angel Politis
la source
Merci pour cette solution, elle est simple et robuste. Une remarque cependant, il y a une petite erreur. La fonction "removeListener" vérifie si le tableau d'événements est vide ou non mais le ternaire est incorrect. Il devrait dire "if (record.listeners [event] .length! = - 1)".
JPA
5

Vous pouvez toujours vérifier manuellement si votre EventListener existe en utilisant l'inspecteur Chrome par exemple. Dans l'onglet Elément, vous avez le sous-onglet traditionnel "Styles" et à proximité un autre: "Écouteurs d'événements". Ce qui vous donnera la liste de tous les EventListeners avec leurs éléments liés.

Paul Leclerc
la source
5

Il semble étrange que cette méthode n'existe pas. Est-il temps de l'ajouter enfin?

Si vous le vouliez, vous pourriez quelque chose comme ce qui suit:

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;
};
1,21 gigawatts
la source
3

Voici un script que j'ai utilisé pour vérifier l'existence d'un écouteur d'événements attaché dynamiquement. J'ai utilisé jQuery pour attacher un gestionnaire d'événements à un élément, puis déclencher cet événement (dans ce cas, l'événement 'click'). De cette façon, je peux récupérer et capturer des propriétés d'événement qui n'existeront que si le gestionnaire d'événements est attaché.

var eventHandlerType;

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

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

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}
dsmith63
la source
2
Vous pouvez également attribuer une propriété à l'élément de votre élément en le marquant essentiellement comme un élément auquel un événement est attaché;
Justin le
2

Il ne semble pas y avoir de fonction de navigateur croisé qui recherche les événements enregistrés sous un élément donné.

Cependant, il est possible de visualiser les fonctions de rappel des éléments dans certains navigateurs en utilisant leurs outils de développement. Cela peut être utile lorsque vous essayez de déterminer le fonctionnement d'une page Web ou pour le débogage du code.

Firefox

Tout d'abord, affichez l'élément dans l' onglet Inspecteur dans les outils de développement. Ceci peut être fait:

  • Sur la page en faisant un clic droit sur l'élément de la page Web que vous souhaitez inspecter et en sélectionnant "Inspecter l'élément" dans le menu.
  • Dans la console en utilisant une fonction pour sélectionner l'élément, telle que document.querySelector , puis en cliquant sur l'icône en regard de l'élément pour l'afficher dans l' onglet Inspecteur .

Si des événements ont été enregistrés dans l'élément, vous verrez un bouton contenant le mot Événement côté de l'élément. En cliquant dessus, vous pourrez voir les événements qui ont été enregistrés avec l'élément. Cliquez sur la flèche à côté d'un événement pour afficher la fonction de rappel correspondant.

Chrome

Tout d'abord, affichez l'élément dans l' onglet Éléments des outils de développement. Ceci peut être fait:

  • Sur la page en faisant un clic droit sur l'élément de la page Web que vous souhaitez inspecter et en sélectionnant «Inspecter» dans le menu
  • Dans la console en utilisant une fonction pour sélectionner l'élément, telle que document.querySelector , en cliquant avec le bouton droit sur l'élément et en sélectionnant «Révéler dans le panneau Eléments» pour l'afficher dans l' onglet Inspecteur .

Près de la section de la fenêtre qui montre l'arborescence contenant les éléments de la page Web, il devrait y avoir une autre section avec un onglet intitulé "Écouteurs d'événements". Sélectionnez-le pour voir les événements enregistrés dans l'élément. Pour voir le code d'un événement donné, cliquez sur le lien à droite de celui-ci.

Dans Chrome, les événements d'un élément peuvent également être trouvés à l'aide de la fonction getEventListeners . Cependant, d'après mes tests, la fonction getEventListeners ne répertorie pas les événements lorsque plusieurs éléments lui sont passés. Si vous souhaitez rechercher tous les éléments de la page qui ont des écouteurs et afficher les fonctions de rappel de ces écouteurs, vous pouvez utiliser le code suivant dans la console pour ce faire:

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

Veuillez modifier cette réponse si vous connaissez des moyens de le faire dans les navigateurs donnés ou dans d'autres navigateurs.

Dave F
la source
1

Je viens de le découvrir en essayant de voir si mon événement était attaché ...

si tu fais :

item.onclick

il retournera "null"

mais si vous faites:

item.hasOwnProperty('onclick')

alors c'est "VRAI"

donc je pense que lorsque vous utilisez "addEventListener" pour ajouter des gestionnaires d'événements, le seul moyen d'y accéder est via "hasOwnProperty". J'aurais aimé savoir pourquoi ou comment mais hélas, après des recherches, je n'ai pas trouvé d'explication.

carinlynchin
la source
2
onclickest distinct de .addEventListener- il affecte un attribut alors que .addEventListenerne le fait pas
Zach Saucier
1

Si je comprends bien, vous pouvez uniquement vérifier si un auditeur a été vérifié, mais pas quel auditeur est spécifiquement présentateur.

Ainsi, un code ad hoc comblerait le vide pour gérer votre flux de codage. Une méthode pratique serait de créer une stateutilisation de variables. Par exemple, attachez le vérificateur d'un auditeur comme suit:

var listenerPresent=false

puis si vous définissez un écouteur, changez simplement la valeur:

listenerPresent=true

puis dans le callback de votre eventListener, vous pouvez attribuer des fonctionnalités spécifiques à l'intérieur et de la même manière, distribuer l'accès aux fonctionnalités en fonction de certains états comme variable par exemple:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true
Webwoman
la source
1

Supprimez simplement l'événement avant de l'ajouter:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
Entretoize
la source
C'est une bonne astuce, mais je suppose que cela ne fonctionne que si vous avez une référence à la fonction qui est l'un des écouteurs d'événements actuellement "ajoutés". Je pense que c'est clairement une lacune de l'API standard, si nous pouvons ajouter un écouteur d'événements, il devrait également être possible de vérifier quels écouteurs d'événements ont été ajoutés jusqu'à présent. C'est presque comme si les écouteurs d'événements sont actuellement des "variables d'écriture uniquement", ce qui semble être un concept obscur.
Panu Logic le
0

Je viens d'écrire un script qui vous permet d'y parvenir. Il vous donne deux fonctions globales: hasEvent(Node elm, String event)et getEvents(Node elm)que vous pouvez utiliser. Sachez que cela modifie la EventTargetméthode prototype add/RemoveEventListeneret ne fonctionne pas pour les événements ajoutés via le balisage HTML ou la syntaxe javascript deelm.on_event = ...

Plus d'informations sur GitHub

Démo en direct

Scénario:

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)}();
Gaurang Tandon
la source
-1

Vous pouvez en théorie superposer addEventListener et removeEventListener pour ajouter remove un indicateur à l'objet «this». Moche et je n'ai pas testé ...

kofifus
la source