JQuery найти обработчики событий, зарегистрированных с объектом

555

Мне нужно найти, какие обработчики событий зарегистрированы для объекта.

Например:

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

$("#el")зарегистрирован клик и наведение мыши .

Есть ли функция, чтобы узнать это и, возможно, перебрать обработчики событий?

Если это невозможно для объекта jQuery через надлежащие методы, возможно ли это для простого объекта DOM?

ages04
источник
5
к сожалению, сейчас: bugs.jquery.com/ticket/10589
Skylar Saveland
2
поддержка jQuery до и после 1.8:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
2
Обратите внимание, что вы можете использовать инструменты разработки FF и Chrome (F12) для просмотра этих прослушивателей событий. См developers.google.com/web/tools/chrome-devtools/debug/... и developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/...
oriadam

Ответы:

691

Начиная с jQuery 1.8, данные событий больше не доступны из «публичного API» для данных. Прочитайте этот пост в блоге jQuery . Теперь вы должны использовать это вместо:

jQuery._data( elem, "events" );

elem должен быть элементом HTML, а не объектом jQuery или селектором.

Обратите внимание, что это внутренняя «частная» структура, которая не должна изменяться. Используйте это только для целей отладки.

В старых версиях jQuery вам, возможно, придется использовать старый метод:

jQuery( elem ).data( "events" );
JPS
источник
222
но вы все еще можете использовать $._data($(elem).get(0), "events")
bullgare
10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data («events»): jQuery сохраняет свои связанные с событием данные в объекте данных с именем (wait for it) events для каждого элемента. Это внутренняя структура данных, поэтому в 1.8 она будет удалена из пространства имен пользовательских данных, поэтому она не будет конфликтовать с элементами с одинаковыми именами. К данным события jQuery все еще можно получить доступ через jQuery._data (элемент, «события»), но имейте в виду, что это внутренняя структура данных, которая не документирована и не должна изменяться.
Сэм Гринхалг,
Я использовал этот метод, чтобы попытаться выяснить событие нажатия кнопки. В консоли Chrome это показывалось handler: function () {внутри свойства click. Мне пришлось дважды щелкнуть мышью на функциональной части, чтобы развернуть и показать все содержимое функции.
Джим
@jim Дааа, ответом будет двойной щелчок
Адиб Арои
2
Безупречная поддержка обоих вариантов:var events = (jQuery._data || jQuery.data)(elem, 'events');
Ориадам
84

Вы можете сделать это путем сканирования событий (начиная с jQuery 1.8+), например:

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

Вот пример, с которым вы можете играть:

$(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>

Ник Крейвер
источник
2
Это правильный ответ. Важнейший момент - вторая половина. Это помогло мне найти проблему в течение минуты, которая заняла бы больше часа, если бы мне пришлось перебирать весь код. Спасибо!
Эндрю Энсли
2
Работает с 1.4, но не в jQuery 1.8.2.
Тимо Кяхконен
15
Для JQuery 1.8+, вы должны использовать метод «приватных данных»: jQuery._data( jQuery("#el")[0], "events" );вместо метода «публичных данных»: jQuery("#el").data("events"). events самом деле объект долго не хранился .data(), мы удалили несколько байтов кода, удалив этот «прокси» из «публичного API»
gnarf
36

В jQuery 1.8+ это больше не будет работать, поскольку внутренние данные помещены в другой объект.

Последний неофициальный (но работает и в предыдущих версиях, по крайней мере, в 1.7.2) способ сделать это сейчас - $._data(element, "events")

Подчеркивание («_») - вот что здесь имеет значение. Внутренне он вызывает $.data(element, name, null, true), последний (четвертый) параметр является внутренним ("pvt").

PhistucK
источник
$ ._ data ("body", "events") undefined $ (). jquery; «1.7.1» (пробовал 1.7.2 и 1.8.1 все время «не определено»)
Марс Робертсон
2
@Michal - api.jquery.com/jQuery.data говорит, что принимает элемент, а не селектор.
PhistucK
1
Теперь работает нормально: $ ._ data ($ ("body"). Get (0), "events") Или даже лучше: $ ("body"). Data ("events")!
Марс Робертсон
2
FWIW - указание на то, что он «внутренне» вызывает другую функцию данных с параметром, который мы специально не задокументируем, вероятно, не нужен. Но да, jQuery._data( element, "events" )это «правильный» способ получить эту информацию сейчас.
gnarf
34

Бесстыдная вилка, но вы можете использовать findHandlerJS

Чтобы использовать его, вы просто должны включить findHandlersJS (или просто скопировать и вставить сырой код JavaScript в окно консоли chrome) и указать тип события и селектор jquery для интересующих вас элементов.

Для вашего примера вы можете быстро найти обработчики событий, которые вы упомянули, выполнив

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

Вот что возвращается:

  • element
    Фактический элемент, в котором обработчик события был зарегистрирован в
  • Events
    Массив с информацией об обработчиках событий jquery для интересующего нас типа события (например, щелчок, изменение и т. д.).
    • обработчик
      Фактический метод обработчика событий, который вы можете увидеть, щелкнув его правой кнопкой мыши и выбрав Показать определение функции
    • селектор
      Селектор, предназначенный для делегированных событий. Для прямых событий будет пусто.

    • список целей с элементами, на которые нацелен этот обработчик событий. Например, для делегированного обработчика событий, который зарегистрирован в объекте документа и предназначается для всех кнопок на странице, это свойство будет перечислять все кнопки на странице. Вы можете навести их и увидеть, что они выделены хромом.

Вы можете попробовать это здесь

Руи
источник
Я думаю, что это должен быть принятый ответ. Это единственный, который работает для меня тоже. Это очень тщательно, так как проходит через все элементы в поисках событий.
Маркиньо Пели
12

Я использую eventbug плагин Firebug для этой цели.

Антон
источник
Спасибо, отличный совет. Расширение добавляет в Firebug вкладку («События»), в которой отображаются события страницы, поэтому вы можете легко их просмотреть.
Грубер
11
Кроме того, в Chrome Developer Tools есть «Прослушиватели событий» на вкладке «Элементы» и «Точки останова прослушивателей событий» на вкладке «Источники».
clayzermk1
10

Я объединил оба решения из @jps в одну функцию:

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

Но будьте осторожны, эта функция может возвращать только те события, которые были установлены с помощью самого jQuery.

Algorhythm
источник
5

Начиная с версии 1.9, нет документированного способа получения событий, кроме использования плагина Migrate для восстановления старого поведения. Вы можете использовать метод _.data (), как упоминает jps, но это внутренний метод. Так что просто сделайте правильно и используйте плагин Migrate, если вам нужна эта функциональность.

Из документации по jQuery .data("events")

До версии 1.9 .data («события») можно было использовать для извлечения недокументированной структуры данных внутренних событий jQuery для элемента, если никакой другой код не определил элемент данных с именем «события». Этот особый случай был удален в 1.9. Нет открытого интерфейса для извлечения этой внутренней структуры данных, и она остается недокументированной. Однако плагин jQuery Migrate восстанавливает это поведение для кода, который зависит от него.

oligofren
источник
Принятый ответ также ясно показывает новый, правильный способ получить его для последних версий: jQuery._data( elem, "events" );...
Ян
2
Частный недокументированный путь никогда не будет правильным . Правильный путь - то есть документированный, публичный и предполагаемый - это использовать плагин Migrate.
oligofren
3
Похоже, вы неправильно поняли суть плагина Migrate. jQuery удалил устаревшие функции, а плагин Migrate должен помочь перенести код разработчика на более новые версии, чтобы они могли сразу воспользоваться новыми функциями и улучшениями, но не потерять функциональность. Он предназначен для того, чтобы помочь кодеру увидеть, что им нужно сделать, чтобы правильно начать использовать новые версии jQuery. Вы не должны использовать его в производстве для восстановления функций. Кроме того, многие вещи не документированы и не обновляются в документации jQuery - они уже указывали на это, так что это не причина
Ian
Кроме того, если бы он был включен в качестве предложения в блоге jQuery, я бы использовал его: blog.jquery.com/2012/08/09/jquery-1-8-released
Ian
Ваши рассуждения о плагине Migrate кажутся разумными. Хорошо, если я удалю свой ответ?
oligofren
5

Чтобы проверить события на элементе:

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

Обратите внимание, что это будет работать только с прямыми обработчиками событий, если вы используете $ (document) .on («имя-события», «jq-selector», function () {// logic}), вы захотите увидеть Функция getEvents внизу этого ответа

Например:

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

или

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

Полный пример:

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

Более полный способ проверки, включающий динамические слушатели, установленные с помощью $ (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;
}

Пример использования:

getEvents($('#myElemId')[0])
Том Г
источник
Этот getEventsметод в целом великолепен, но дело в том, что он дает повторяющиеся записи в IE11 (я знаю, IE снова, но корпоративная необходимость в этом ...). РЕДАКТИРОВАТЬ: $ ._ данные содержат дубликаты событий для элемента, хотя в FF он не содержит ... странный мир IE. Но важно остерегаться возможности повторения событий.
Доминик Шиманьский
О, Том, на самом деле твой код умножает события при каждом выполнении этого метода. Фигово.
Доминик Шиманский
4

Я создал пользовательский селектор jQuery, который проверяет как кеш jQuery назначенных обработчиков событий, так и элементы, которые используют собственный метод для их добавления:

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

Пример:

$(":event(click)")

Это вернет элементы, к которым прикреплен обработчик кликов.

Erutan409
источник
2

В современном браузере с ECMAScript 5.1 / Array.prototype.mapвы также можете использовать

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

в консоли браузера, которая будет печатать исходные коды обработчиков, разделенных запятой. Полезно, чтобы взглянуть на то, что все работает на определенном событии.

Джезан Фафон
источник
jQuery._data('ct100_ContentPlaceHolder1_lcsSection','events')["EVENT_NAME"].map(function(elem){return elem.handler;}); Uncaught TypeError: Невозможно прочитать свойство 'EVENT_NAME' из undefined в <anonymous>: 1: 62
Майк W
'ct100_ContentPlaceHolder1_lcsSection'это строка, а не элемент DOM.
Джезан Фафон
2

События могут быть получены с помощью:

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

или jQuery 1.8+:

jQuery._data(elem, 'events');

Примечание: события, ограниченные использованием, $('selector').live('event', handler) могут быть получены с помощью:

jQuery(document).data('events')
Р. Остерхолт
источник
2
jQuery (документ) .data ('события') дает мне неопределенный
Майк W
1

Я должен сказать, что многие ответы интересны, но недавно у меня возникла похожая проблема, и решение было очень простым, если идти по DOM. Это отличается тем, что вы не повторяете, а целитесь непосредственно в нужное вам событие, но ниже я дам более общий ответ.

У меня было изображение в ряд:

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

И к этому изображению был прикреплен обработчик события click:

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

Мое намерение состояло в том, чтобы расширить область нажатия на всю строку, поэтому я сначала получил все изображения и относительные строки:

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

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

Теперь в фактической строке ответа я просто сделал следующее, дав ответ на оригинальный вопрос:

tr.click(this.onclick);

Поэтому я извлек обработчик событий непосредственно из элемента DOM и поместил его в обработчик событий нажатия jQuery. Работает как шарм.

Теперь к общему случаю. В прежние дни до jQuery вы могли привязать все события к объекту с помощью двух простых, но мощных функций, подаренных нам, смертным, Дугласом Крокфордом :

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;
      }
    }
  });
}
ПИД-регулятор
источник
0

Другой способ сделать это - просто использовать jQuery для захвата элемента, а затем пройти через настоящий Javascript, чтобы получить, установить и поиграть с обработчиками событий. Например:

var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;
tempranova
источник
1
Я думаю, что jQuery выполняет оптимизацию обработки событий, которая, как мне кажется, обходится вашим кодом здесь.
Эмиль Бержерон
Спасибо, да, у меня было чувство, что это взломано - есть ли хорошая ссылка, чтобы узнать больше об этой оптимизации?
tempranova
0

Я объединил некоторые из ответов выше и создал этот безумно выглядящий, но функциональный скрипт, который, как мы надеемся, перечисляет большинство слушателей событий для данного элемента. Не стесняйтесь оптимизировать это здесь.

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>

Марек Лисы
источник
0

jQuery не позволяет вам просто получить доступ к событиям для данного элемента. Вы можете получить к ним доступ, используя недокументированный внутренний метод

$._data(element, "events")

Но он все равно не даст вам все события, а точнее не покажет вам события, назначенные с

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

Эти события хранятся внутри документа, поэтому вы можете получить их, просматривая

$._data(document, "events")

но это тяжелая работа, поскольку есть события для всей веб-страницы.

Том Г создал созданную функцию, которая фильтрует документ только по событиям данного элемента и объединяет выходные данные обоих методов, но у него есть недостаток дублирования событий в выходных данных (и, по сути, из-за того, что внутренний список событий jQuery связывается с вашим приложением). Я исправил этот недостаток, и вы можете найти код ниже. Просто вставьте его в консоль разработчика или в код приложения и выполните его, когда необходимо, чтобы получить хороший список всех событий для данного элемента.

Что важно отметить, элемент на самом деле является HTMLElement, а не объектом 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;
}
Доминик Шиманский
источник