Como verificar se existe ouvinte de eventos anexado dinamicamente ou não?

103

Aqui está o meu problema: é possível verificar de alguma forma a existência de um ouvinte de evento anexado dinamicamente? Ou como posso verificar o status da propriedade "onclick" (?) No DOM? Eu pesquisei na Internet como Stack Overflow por uma solução, mas não tive sorte. Aqui está o meu html:

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

Então, em Javascript, anexo ouvinte de eventos criado dinamicamente ao segundo link:

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

O código funciona bem, mas todas as minhas tentativas de detectar esse listener anexado falham:

// 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 está aqui . Se você clicar em "Adicionar onclick para 2" e depois em "[link 2]", o evento dispara bem, mas o "Link de teste 2" sempre informa falso. Alguém pode ajudar?

Stano
fonte
1
Lamento dizer, mas é impossível obter as associações de evento usando seu método atual: stackoverflow.com/questions/5296858/…
Ivan
1
você pode fazer isso usando a ferramenta de desenvolvimento do cromo: stackoverflow.com/a/41137585/863115
arufian
Você pode fazer isso criando seu próprio armazenamento que mantém o controle dos ouvintes. Veja minha resposta para mais.
Angel Politis
Se o objetivo é evitar que um evento que já foi adicionado seja adicionado novamente, a resposta certa está aqui . Basicamente, se você usar uma função nomeada em vez de uma anônima, ouvintes de eventos idênticos duplicados serão descartados e você não precisa se preocupar com eles.
Syknapse
Se você estiver preocupado em ter ouvintes duplicados, remova o ouvinte de evento atual antes de adicioná-lo novamente. Não é perfeito, mas é simples.
Jacksonkr

Respostas:

49

Não há como verificar se existem ouvintes de eventos anexados dinamicamente ou não.

A única maneira de ver se um ouvinte de evento está anexado é anexando ouvintes de evento como este:

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

Você pode então testar se um ouvinte de evento foi anexado onclick, retornando !!elem.onclick(ou algo semelhante).

Ivan
fonte
33

Eu fiz algo assim:

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

Criar um atributo especial para um elemento quando o ouvinte é anexado e, em seguida, verificar se ele existe.

Karol Grabowski
fonte
4
Esta é a abordagem que usei no passado. Minha recomendação seria usar uma estrutura de sintaxe muito específica. IE, em vez de "ouvinte", use o próprio evento. Então, "data-evento-clique". Isso fornece alguma flexibilidade no caso de você estar procurando fazer mais de um evento e mantém as coisas um pouco mais legíveis.
conrad10781
Isso com a adição de @ conrad10781 parece ser a maneira mais confiável e simples. Se, por algum motivo, o elemento for reenviado e o ouvinte de evento for desconectado, este atributo também será redefinido.
Arye Eidelman
23

O que eu faria é criar um booleano fora da sua função que começa como FALSE e é definido como TRUE quando você anexa o evento. Isso serviria como uma espécie de sinalizador para você antes de anexar o evento novamente. Aqui está um exemplo da ideia.

// 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);
 }
}
Cesar
fonte
Isso requer que você altere o código do evento anexado para controlar se ele está anexado (semelhante a esta ou esta resposta ). Não funcionará se você não controlar o código.
user202729
6

tl; dr : Não, você não pode fazer isso de nenhuma forma com suporte nativo.


A única maneira que conheço de fazer isso é criar um objeto de armazenamento personalizado onde você mantém um registro dos ouvintes adicionados. Algo nas seguintes linhas:

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

Etapa 1: primeiro, você precisará de uma função que possa percorrer o objeto de armazenamento e retornar o registro de um elemento dado o elemento (ou falso).

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

Etapa 2: Em seguida, você precisará de uma função que possa adicionar um ouvinte de evento, mas também inserir o ouvinte no objeto de armazenamento.

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

Etapa 3: Com relação ao requisito real de sua pergunta, você precisará da seguinte função para verificar se um elemento foi adicionado como ouvinte de evento para um evento específico.

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

Etapa 4: finalmente, você precisará de uma função que possa excluir um ouvinte do objeto de armazenamento.

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

Snippet:


Embora mais de 5 anos tenham se passado desde que o OP postou a pergunta, acredito que as pessoas que toparem com ela no futuro se beneficiarão com esta resposta, então fique à vontade para fazer sugestões ou melhorias. 😊

Angel Politis
fonte
Obrigado por esta solução, é simples e robusta. Uma observação, porém, há um pequeno erro. A função "removeListener" verifica se o array de eventos está vazio ou não, mas o ternário está incorreto. Deve dizer "if (record.listeners [event] .length! = - 1)".
JPA de
5

Você sempre pode verificar manualmente se o EventListener existe usando o inspetor do Chrome, por exemplo. Na aba Elemento você tem a tradicional sub-aba "Estilos" e perto dela outra: "Ouvintes de Eventos". O que lhe dará a lista de todos os EventListeners com seus elementos vinculados.

Paul Leclerc
fonte
5

Parece estranho que esse método não exista. É hora de adicioná-lo finalmente?

Se você quisesse, poderia algo como o seguinte:

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
fonte
3

Aqui está um script que usei para verificar a existência de um ouvinte de evento anexado dinamicamente. Usei o jQuery para anexar um manipulador de eventos a um elemento e, em seguida, acionar esse evento (neste caso, o evento 'click'). Dessa forma, posso recuperar e capturar propriedades de eventos que só existirão se o manipulador de eventos estiver conectado.

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
fonte
2
Você também pode atribuir uma propriedade ao elemento - seu elemento, basicamente, marcando-o como um elemento que possui um evento anexado a ele;
Justin
2

Não parece haver uma função de navegador cruzado que procura eventos registrados em um determinado elemento.

No entanto, é possível visualizar as funções de retorno de chamada para elementos em alguns navegadores usando suas ferramentas de desenvolvimento. Isso pode ser útil ao tentar determinar como uma página da web funciona ou para depurar código.

Raposa de fogo

Primeiro, visualize o elemento na guia Inspetor nas ferramentas do desenvolvedor. Isto pode ser feito:

  • Na página , clique com o botão direito do mouse no item da página da web que você deseja inspecionar e selecione "Inspecionar elemento" no menu.
  • No console , use uma função para selecionar o elemento, como document.querySelector , e clique no ícone ao lado do elemento para visualizá-lo na guia Inspetor .

Se algum evento foi registrado para o elemento, você verá um botão contendo a palavra Evento ao lado do elemento. Clicar nele permitirá que você veja os eventos que foram registrados com o elemento. Clicar na seta ao lado de um evento permite visualizar a função de retorno de chamada para ele.

cromada

Primeiro, visualize o elemento na guia Elementos nas ferramentas do desenvolvedor. Isto pode ser feito:

  • Na página , clicando com o botão direito do mouse no item da página da web que você deseja inspecionar e selecionando "Inspecionar" no menu
  • No console , use uma função para selecionar o elemento, como document.querySelector , clicando com o botão direito do mouse no elemento e selecionando "Revelar no painel Elementos" para visualizá-lo na guia Inspetor .

Perto da seção da janela que mostra a árvore que contém os elementos da página da web, deve haver outra seção com uma guia intitulada "Ouvintes de eventos". Selecione-o para ver os eventos que foram registrados no elemento. Para ver o código de um determinado evento, clique no link à direita dele.

No Chrome, os eventos de um elemento também podem ser encontrados usando a função getEventListeners . No entanto, com base em meus testes, a função getEventListeners não lista eventos quando vários elementos são passados ​​para ela. Se quiser encontrar todos os elementos na página que têm ouvintes e visualizar as funções de retorno de chamada para esses ouvintes, você pode usar o seguinte código no console para fazer isso:

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

Edite esta resposta se você souber de maneiras de fazer isso nos navegadores fornecidos ou em outros navegadores.

Dave F
fonte
1

Acabei de descobrir isso tentando ver se meu evento foi anexado ...

se você fizer :

item.onclick

ele retornará "nulo"

mas se você fizer:

item.hasOwnProperty('onclick')

então é "VERDADEIRO"

então eu acho que quando você usa "addEventListener" para adicionar manipuladores de eventos, a única maneira de acessá-lo é por meio de "hasOwnProperty". Gostaria de saber por que ou como, mas, depois de pesquisar, não encontrei uma explicação.

Carinlynchin
fonte
2
onclické separado de .addEventListener- afeta um atributo, enquanto .addEventListenernão
Zach Saucier
1

Se bem entendi, você só pode verificar se um ouvinte foi verificado, mas não qual ouvinte é o apresentador especificamente.

Portanto, algum código ad hoc iria preencher a lacuna para lidar com seu fluxo de codificação. Um método prático seria criar um stateusando variáveis. Por exemplo, anexe um verificador de ouvinte da seguinte forma:

var listenerPresent=false

então, se você definir um ouvinte, basta alterar o valor:

listenerPresent=true

então, dentro do callback de seu eventListener você pode atribuir funcionalidades específicas dentro e da mesma forma, distribuir o acesso a funcionalidades dependendo de algum estado como variável por exemplo:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true
Webwoman
fonte
1

Basta remover o evento antes de adicioná-lo:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
Entretoize
fonte
Esse é um bom truque, mas presumo que funcione apenas se você tiver um ref para a função que é um dos ouvintes de eventos "adicionados" atualmente. Eu acho que isso é claramente uma lacuna da API padrão, se podemos adicionar qualquer ouvinte de evento, também deve ser possível verificar quais ouvintes de evento foram adicionados até agora. É quase como se os ouvintes de eventos atualmente fossem "variáveis ​​somente de gravação", o que parece um conceito obscuro.
Panu Logic
0

Acabei de escrever um script que permite que você faça isso. Ele oferece duas funções globais: hasEvent(Node elm, String event)e getEvents(Node elm)que você pode utilizar. Esteja ciente de que ele modifica o EventTargetmétodo do protótipo add/RemoveEventListenere não funciona para eventos adicionados por meio de marcação HTML ou sintaxe javascript deelm.on_event = ...

Mais informações no GitHub

Demonstração ao vivo

Roteiro:

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
fonte
-1

Você poderia, em teoria, carregar addEventListener e removeEventListener para adicionar remove um sinalizador ao objeto 'este'. Feio e eu não testei ...

kofifus
fonte