jQuery encontrar manipuladores de eventos registrados com um objeto

555

Preciso descobrir quais manipuladores de eventos estão registrados sobre um objeto.

Por exemplo:

$("#el").click(function() {...});
$("#el").mouseover(function() {...});

$("#el")tem clique e mouseover registrado.

Existe uma função para descobrir isso e possivelmente interagir com os manipuladores de eventos?

Se não for possível em um objeto jQuery através de métodos adequados, é possível em um objeto DOM simples?

idades04
fonte
5
infelizmente, agora: bugs.jquery.com/ticket/10589
Skylar Saveland
2
suporta jQuery pré e pós 1.8:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
2
Observe que você pode usar as ferramentas de desenvolvimento do FF e do Chrome (a F12) para ver esses ouvintes de eventos. Veja developers.google.com/web/tools/chrome-devtools/debug/… e developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/…
oriadam

Respostas:

691

A partir do jQuery 1.8, os dados do evento não estão mais disponíveis na "API pública" para dados. Leia esta postagem do blog jQuery . Agora você deve usar isso:

jQuery._data( elem, "events" );

elem deve ser um elemento HTML, não um objeto jQuery ou seletor.

Observe que esta é uma estrutura interna, 'privada' e não deve ser modificada. Use isso apenas para fins de depuração.

Nas versões mais antigas do jQuery, talvez você precise usar o método antigo, que é:

jQuery( elem ).data( "events" );
jps
fonte
222
mas você ainda pode usar $._data($(elem).get(0), "events")
bullgare
10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data (“events”): o jQuery armazena seus dados relacionados a eventos em um objeto de dados chamado (aguarde) eventos em cada elemento. Essa é uma estrutura de dados interna; portanto, na versão 1.8, ela será removida do espaço de nome de dados do usuário para não entrar em conflito com itens com o mesmo nome. Os dados do evento do jQuery ainda podem ser acessados ​​via jQuery._data (elemento, "events"), mas lembre-se de que essa é uma estrutura de dados interna que não está documentada e não deve ser modificada.
amigos estão
Eu tenho usado esse método para tentar descobrir o evento de clique de um botão. No console do Chrome, ele era mostrado handler: function () {dentro da propriedade click. Eu tive que clicar duas vezes na parte da função para expandir e mostrar o conteúdo completo da função.
Jim
@ Jim yeaaah, o duplo clique é a resposta
Adib Aroui
2
Suporte perfeitamente às duas opções:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
84

Você pode fazer isso rastreando os eventos (a partir do jQuery 1.8+), assim:

$.each($._data($("#id")[0], "events"), function(i, event) {
  // i is the event type, like "click"
  $.each(event, function(j, h) {
    // h.handler is the function being called
  });
});

Aqui está um exemplo com o qual você pode brincar:

$(function() {
  $("#el").click(function(){ alert("click"); });
  $("#el").mouseover(function(){ alert("mouseover"); });

  $.each($._data($("#el")[0], "events"), function(i, event) {
    output(i);
    $.each(event, function(j, h) {
        output("- " + h.handler);
    });
  });
});

function output(text) {
    $("#output").html(function(i, h) {
        return h + text + "<br />";
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="el">Test</div>
<code>
    <span id="output"></span>
</code>

Nick Craver
fonte
2
Essa é a resposta correta. O ponto crucial é a segunda metade. Isso me ajudou a encontrar um problema em menos de um minuto que levaria mais de uma hora se eu tivesse que pesquisar em todo o código. Obrigado!
Andrew Ensley
2
Funciona com 1.4, mas não no jQuery 1.8.2.
Timo Kähkönen
15
Para jQuery 1.8+, você deve usar o método 'dados privados': jQuery._data( jQuery("#el")[0], "events" );em vez do método 'dados públicos': jQuery("#el").data("events"). O eventsobjeto realmente não foi armazenado em .data()por um longo tempo, nós aparadas alguns bytes de código por querer retirar esta "proxy" da "API pública"
gnarf
36

Para o jQuery 1.8+, isso não funcionará mais porque os dados internos são colocados em um objeto diferente.

A última maneira não oficial (mas funciona também nas versões anteriores, pelo menos na versão 1.7.2) agora é: $._data(element, "events")

O sublinhado ("_") é o que faz a diferença aqui. Internamente, está chamando $.data(element, name, null, true), o último (quarto) parâmetro é interno ("pvt").

PhistucK
fonte
$ ._ data ("corpo", "eventos") indefinido $ (). jquery; "1.7.1" (tente 1.7.2 e 1.8.1 o tempo todo "indefinido")
Mars Robertson
2
@Michal - api.jquery.com/jQuery.data diz que aceita um elemento, não um seletor.
PhistucK
1
Agora funciona bem: $ ._ data ($ ("body"). Get (0), "events") Ou melhor ainda: $ ("body"). Data ("events")!
Mars Robertson
2
FWIW - Assinalar que 'internamente' chama a outra função de dados com um parâmetro que especificamente não documentamos provavelmente não é necessário. Mas sim, jQuery._data( element, "events" )é a maneira 'correta' de obter essas informações agora.
Gnarf
34

Plugue sem vergonha, mas você pode usar o findHandlerJS

Para usá-lo, basta incluir findHandlersJS (ou simplesmente copiar e colar o código javascript bruto na janela do console do chrome) e especificar o tipo de evento e um seletor jquery para os elementos nos quais você está interessado.

Para o seu exemplo, você pode encontrar rapidamente os manipuladores de eventos mencionados, fazendo

findEventHandlers("click", "#el")
findEventHandlers("mouseover", "#el")

É isso que é retornado:

  • elemento
    O elemento real em que o manipulador de eventos foi registrado

  • matriz de eventos com informações sobre os manipuladores de eventos jquery para o tipo de evento em que estamos interessados ​​(por exemplo, clique, altere etc.)
    • manipulador
      Método real do manipulador de eventos que você pode ver clicando com o botão direito do mouse e selecionando Mostrar definição de função
    • seletor
      O seletor fornecido para eventos delegados. Estará vazio para eventos diretos.
    • destinos
      Lista com os elementos que esse manipulador de eventos tem como alvo. Por exemplo, para um manipulador de eventos delegados registrado no objeto de documento e direcionado a todos os botões em uma página, essa propriedade listará todos os botões na página. Você pode passar o mouse e vê-los realçados no chrome.

Você pode tentar aqui

Rui
fonte
Eu acho que essa deve ser a resposta aceita. Esse é o único que funciona para mim também. É muito completo, pois percorre todos os elementos em busca de eventos.
Marquinho Peli
12

Eu uso o plugin eventbug para firebug para esse fim.

Anton
fonte
Obrigado, ótima dica. A extensão adiciona uma guia ao Firebug ("Eventos") que mostra os eventos da página, para que você possa explorá-los facilmente.
Gruber
11
Além disso, as Ferramentas do desenvolvedor do Chrome têm "Ouvintes de eventos" na guia "Elementos" e "Pontos de interrupção do ouvinte de eventos" na guia "Fontes".
clayzermk1
10

Combinei as duas soluções de @jps em uma função:

jQuery.fn.getEvents = function() {
    if (typeof(jQuery._data) === 'function') {
        return jQuery._data(this.get(0), 'events') || {};
    }

    // jQuery version < 1.7.?
    if (typeof(this.data) === 'function') {
        return this.data('events') || {};
    }

    return {};
};

Mas cuidado, essa função pode retornar apenas eventos que foram definidos usando o próprio jQuery.

algorítmico
fonte
5

A partir da versão 1.9, não há maneira documentada de recuperar os eventos, além de usar o plug-in Migrar para restaurar o comportamento antigo. Você pode usar o método _.data () como o jps menciona, mas esse é um método interno. Portanto, faça a coisa certa e use o plug-in Migrar se precisar dessa funcionalidade.

Na documentação do jQuery em .data("events")

Antes de 1.9, .data ("events") podia ser usado para recuperar a estrutura de dados de eventos internos não documentados do jQuery para um elemento se nenhum outro código tivesse definido um elemento de dados com o nome "events". Este caso especial foi removido em 1.9. Não há interface pública para recuperar essa estrutura de dados interna e ela permanece sem documentos. No entanto, o plug-in jQuery Migrate restaura esse comportamento para o código que depende dele.

oligofren
fonte
A resposta aceita também mostra claramente a nova e correta maneira de obtê-lo por versões recentes: jQuery._data( elem, "events" );...
Ian
2
Uma maneira privada e sem documentos nunca será a maneira correta . A maneira correta - significando documentado, público e pretendido - é usar o plug-in Migrar.
Oligofren
3
Você parece entender mal o ponto do plug-in Migrar. O jQuery removeu os recursos obsoletos e o plug-in Migrar é para ajudar a migrar o código do desenvolvedor para as versões mais recentes, para que eles possam tirar proveito imediato dos novos recursos e aprimoramentos, mas não perder a funcionalidade. Ele serve para ajudar o codificador a ver o que ele precisa fazer para começar a usar corretamente as novas versões do jQuery. Você não deve usá-lo em produção para restaurar recursos. Além disso, muitas coisas não são documentados e até à data na documentação jQuery - eles apontaram-lo antes, de modo que não é uma razão
Ian
Além disso, se ele está incluído como uma sugestão no blog jQuery, eu usá-lo: blog.jquery.com/2012/08/09/jquery-1-8-released
Ian
Seu raciocínio no plug-in Migrar parece razoável. OK se eu excluir minha resposta?
Oligofren 28/05
5

Para verificar se há eventos em um elemento:

var events = $._data(element, "events")

Observe que isso só funcionará com manipuladores de eventos diretos. Se você estiver usando $ (document) .on ("nome do evento", "jq-selector", function () {// logic}), será necessário ver o função getEvents na parte inferior desta resposta

Por exemplo:

 var events = $._data(document.getElementById("myElemId"), "events")

ou

 var events = $._data($("#myElemId")[0], "events")

Exemplo completo:

<html>
    <head>
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
        <script>
            $(function() {
                $("#textDiv").click(function() {
                    //Event Handling
                });
                var events = $._data(document.getElementById('textDiv'), "events");
                var hasEvents = (events != null);
            });
        </script>
    </head>
    <body>
        <div id="textDiv">Text</div>
    </body>
</html>

Uma maneira mais completa de verificar, que inclui ouvintes dinâmicos, instalados com $ (document) .on

function getEvents(element) {
    var elemEvents = $._data(element, "events");
    var allDocEvnts = $._data(document, "events");
    for(var evntType in allDocEvnts) {
        if(allDocEvnts.hasOwnProperty(evntType)) {
            var evts = allDocEvnts[evntType];
            for(var i = 0; i < evts.length; i++) {
                if($(element).is(evts[i].selector)) {
                    if(elemEvents == null) {
                        elemEvents = {};
                    }
                    if(!elemEvents.hasOwnProperty(evntType)) {
                        elemEvents[evntType] = [];
                    }
                    elemEvents[evntType].push(evts[i]);
                }
            }
        }
    }
    return elemEvents;
}

Exemplo de uso:

getEvents($('#myElemId')[0])
Tom G
fonte
Esse getEventsmétodo é muito bom, mas o fato é que ele fornece entradas duplicadas no IE11 (eu sei, o IE novamente, mas a empresa precisa dele ...). EDIT: $ ._ data contém eventos duplicados para o elemento, embora no FF não contenha nenhum ... Mundo estranho do IE. Mas é importante estar atento a essa possibilidade de ter eventos duplicados.
Dominik Szymański
Ah, Tom, na verdade é o seu código que multiplica eventos a cada execução desse método. Não é bom.
Dominik Szymański
4

Criei um seletor jQuery personalizado que verifica o cache do jQuery de manipuladores de eventos atribuídos, bem como elementos que usam o método nativo para adicioná-los:

(function($){

    $.find.selectors[":"].event = function(el, pos, match) {

        var search = (function(str){
            if (str.substring(0,2) === "on") {str = str.substring(2);}
            return str;
        })(String(match[3]).trim().toLowerCase());

        if (search) {
            var events = $._data(el, "events");
            return ((events && events.hasOwnProperty(search)) || el["on"+search]);
        }

        return false;

    };

})(jQuery);

Exemplo:

$(":event(click)")

Isso retornará elementos que possuem um manipulador de cliques anexado a eles.

Erutan409
fonte
2

Em um navegador moderno com ECMAScript 5.1 / Array.prototype.map, você também pode usar

jQuery._data(DOCUMENTELEMENT,'events')["EVENT_NAME"].map(function(elem){return elem.handler;});

no console do navegador, que imprimirá a origem dos manipuladores, delimitados por vírgula. Útil para olhar o que está acontecendo em um evento específico.

Jesan Fafon
fonte
jQuery._data('ct100_ContentPlaceHolder1_lcsSection','events')["EVENT_NAME"].map(function(elem){return elem.handler;}); Não detectado TypeError: Não é possível ler a propriedade 'EVENT_NAME' de indefinida em <anonymous>: 1: 62
Mike W
'ct100_ContentPlaceHolder1_lcsSection'é uma sequência, não um elemento DOM.
Jesan Fafon
2

Os eventos podem ser recuperados usando:

jQuery(elem).data('events');

ou jQuery 1.8+:

jQuery._data(elem, 'events');

Nota: Eventos limitados usando $('selector').live('event', handler) podem ser recuperados usando:

jQuery(document).data('events')
R. Oosterholt
fonte
2
jQuery (document) .data ('events') me dá indefinido #
Mike W
1

Devo dizer que muitas das respostas são interessantes, mas recentemente tive um problema semelhante e a solução foi extremamente simples, seguindo o caminho do DOM. É diferente porque você não itera, mas mira diretamente no evento que precisa, mas abaixo darei uma resposta mais geral.

Eu tive uma imagem em uma fileira:

<table>
  <td><tr><img class="folder" /></tr><tr>...</tr></td>
</table>

E essa imagem tinha um manipulador de eventos de clique anexado a ela:

imageNode.click(function () { ... });

Minha intenção era expandir a área clicável para toda a linha, então primeiro obtive todas as imagens e linhas relativas:

tableNode.find("img.folder").each(function () {
  var tr;

  tr = $(this).closest("tr");
  // <-- actual answer
});

Agora, na linha de resposta real, fiz o seguinte, dando uma resposta à pergunta original:

tr.click(this.onclick);

Então, busquei o manipulador de eventos diretamente do elemento DOM e o coloquei no manipulador de eventos jQuery click. Funciona como um encanto.

Agora, para o caso geral. Nos velhos tempos pré-jQuery, você podia anexar todos os eventos a um objeto com duas funções simples, porém poderosas, concedidas a nós mortais por Douglas Crockford :

function walkTheDOM(node, func)
{
  func(node);
  node = node.firstChild;
  while (node)
  {
    walkTheDOM(node, func);
    node = node.nextSibling;
  }
}

function purgeEventHandlers(node)
{
  walkTheDOM(node, function (n) {
    var f;

    for (f in n)
    {
      if (typeof n[f] === "function")
      {
        n[f] = null;
      }
    }
  });
}
pid
fonte
0

Outra maneira de fazer isso é usar o jQuery para pegar o elemento e, em seguida, passar pelo Javascript real para obter e definir e jogar com os manipuladores de eventos. Por exemplo:

var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;
tempranova
fonte
1
Eu acho que o jQuery faz otimização de manipulação de eventos que eu acho que é contornada pelo seu código aqui.
Emile Bergeron
Obrigado, sim, eu senti que isso era hacky - algum bom link para saber mais sobre essa otimização?
tempranova
0

Combinei algumas das respostas acima e criei esse script maluco, mas funcional, que lista esperançosamente a maioria dos ouvintes de eventos no elemento especificado. Sinta-se livre para otimizá-lo aqui.

var element = $("#some-element");

// sample event handlers
element.on("mouseover", function () {
  alert("foo");
});

$(".parent-element").on("mousedown", "span", function () {
  alert("bar");
});

$(document).on("click", "span", function () {
  alert("xyz");
});

var collection = element.parents()
  .add(element)
  .add($(document));
collection.each(function() {
  var currentEl = $(this) ? $(this) : $(document);
  var tagName = $(this)[0].tagName ? $(this)[0].tagName : "DOCUMENT";
  var events = $._data($(this)[0], "events");
  var isItself = $(this)[0] === element[0]
  if (!events) return;
  $.each(events, function(i, event) {
    if (!event) return;
    $.each(event, function(j, h) {
      var found = false;        
      if (h.selector && h.selector.length > 0) {
        currentEl.find(h.selector).each(function () {
          if ($(this)[0] === element[0]) {
            found = true;
          }
        });
      } else if (!h.selector && isItself) {
        found = true;
      }

      if (found) {
        console.log("################ " + tagName);
        console.log("event: " + i);
        console.log("selector: '" + h.selector + "'");
        console.log(h.handler);
      }
    });
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="parent-element">
  <span id="some-element"></span>
</div>

Marek Lisý
fonte
0

O jQuery não permite que você simplesmente acesse os eventos de um determinado elemento. Você pode acessá-los usando o método interno não documentado

$._data(element, "events")

Mas ainda não fornecerá todos os eventos, para ser mais preciso, não mostrará os eventos atribuídos com

$([selector|element]).on()

Esses eventos são armazenados no documento, para que você possa buscá-los navegando pelas

$._data(document, "events")

mas isso é um trabalho árduo, pois há eventos para toda a página da web.

Tom G acima criou a função que filtra o documento apenas para eventos de determinado elemento e mescla a saída dos dois métodos, mas havia uma falha na duplicação de eventos na saída (e efetivamente na lista de eventos internos do elemento jQuery que interfere no seu aplicativo). Corrigi essa falha e você pode encontrar o código abaixo. Basta colar no console do desenvolvedor ou no código do aplicativo e executá-lo quando necessário para obter uma boa lista de todos os eventos para um determinado elemento.

O que é importante notar, o elemento é realmente HTMLElement, não o objeto jQuery.

function getEvents(element) {
    var elemEvents = $._data(element, "events");
    var allDocEvnts = $._data(document, "events");
    function equalEvents(evt1, evt2)
    {
        return evt1.guid === evt2.guid;
    }

    for(var evntType in allDocEvnts) {
        if(allDocEvnts.hasOwnProperty(evntType)) {
            var evts = allDocEvnts[evntType];
            for(var i = 0; i < evts.length; i++) {
                if($(element).is(evts[i].selector)) {
                    if(elemEvents == null) {
                        elemEvents = {};
                    }
                    if(!elemEvents.hasOwnProperty(evntType)) {
                        elemEvents[evntType] = [];
                    }
                    if(!elemEvents[evntType].some(function(evt) { return equalEvents(evt, evts[i]); })) {
                        elemEvents[evntType].push(evts[i]);
                    }
                }
            }
        }
    }
    return elemEvents;
}
Dominik Szymański
fonte