jQuery знайде обробників подій, зареєстрованих об'єктом


555

Мені потрібно знайти, які обробники подій зареєстровані над об’єктом.

Наприклад:

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

$("#el")має кнопку і MouseOver зареєстрований.

Чи є якась функція дізнатися це і, можливо, повторити його над обробниками подій?

Якщо неможливо на об’єкті jQuery належними методами, чи можливо це на простому об'єкті DOM?


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

Відповіді:


692

Станом на jQuery 1.8, дані про подію більше не доступні в "загальнодоступному API" для даних. Прочитайте цю публікацію в блозі jQuery . Тепер слід використовувати це замість:

jQuery._data( elem, "events" );

elem має бути елементом HTML, а не об'єктом або селектором jQuery.

Зауважте, що це внутрішня, "приватна" структура, і її не слід змінювати. Використовуйте це лише для налагодження.

У старих версіях jQuery, можливо, доведеться використовувати старий метод, який є:

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

222
але ви все одно можете скористатися $._data($(elem).get(0), "events")
бульгар

10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data ("події"): jQuery зберігає свої дані, пов'язані з подіями, в об'єкт даних, названий (чекайте цього) події на кожному елементі. Це внутрішня структура даних, тому в 1.8 це буде видалено з простору імен користувачів, щоб не суперечити предметам з тим самим іменем. До даних про події jQuery все ще можна отримати доступ через jQuery._data (елемент, "події"), але пам’ятайте, що це внутрішня структура даних, яка недокументована і не повинна змінюватися.
Сем Грінхалг

Я використовував цей метод, щоб спробувати з’ясувати подію натискання кнопки. На консолі Chrome він показав handler: function () {усередині властивості клацання. Мені довелося двічі клацнути на функціональній частині, щоб вона розширювалася та показувала повний вміст функції.
Джим

@jim yeaaah, подвійне клацання - це відповідь
Адіб Аруй

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").


$ ._ дані ("тіло", "події") не визначено $ (). 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 ("події")!
Марс Робертсон

2
FWIW - Вказуючи, що він "внутрішньо" викликає іншу функцію даних з параметром, який ми конкретно не документуємо, ймовірно, не потрібен. Але так, jQuery._data( element, "events" )це "правильний" спосіб отримати цю інформацію зараз.
gnarf

34

Безсоромний штекер, але ви можете використовувати findHandlerJS

Для його використання потрібно просто включити findHandlersJS (або просто скопіювати і вставити необроблений код JavaScript у вікно консолі хрому) та вказати тип події та селектор jquery для елементів, які вас цікавлять.

Для вашого прикладу ви можете швидко знайти обробників подій, про яких ви згадали

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

Ось що повертається:

  • елемент
    Фактичний елемент, в якому був зареєстрований обробник подій
  • події
    Масив з інформацією про обробників JQuery подій для даного типу подій , які ми зацікавлені в (наприклад , клацання, зміна і т.д.)
    • обробник
      Метод обробника фактичних подій, який ви можете побачити, клацнувши правою кнопкою миші та вибравши Показати визначення функції
    • селектор
      Селектор надав делеговані події. Він буде порожнім для прямих подій.

    • Список цілей з елементами, на які спрямований цей обробник подій. Наприклад, для делегованого обробника подій, який зареєстрований у об'єкті документа та орієнтований на всі кнопки сторінки, це властивість буде перелічувати всі кнопки сторінки. Ви можете навести на них курсор і побачити їх виділеними хромом.

Ви можете спробувати тут


Я думаю, що це має бути прийнята відповідь. Це єдине, що працює і для мене. Це дуже ретельно, оскільки проходить усі елементи пошуку подій.
Marquinho Peli

12

Я використовую eventbug плагін Firebug для цієї мети.


Дякую, чудова порада. Розширення додає вкладку до Firebug ("Події"), яка відображає події сторінки, тому ви можете їх легко виправити.
Грубер

11
Також у Інструментах для розробників Chrome є "Слухачі подій" на вкладці "Елементи" та "Точки переривання слухача подій" на вкладці "Джерела".
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.


5

Станом на 1.9 немає жодного документально підтвердженого способу відновлення подій, окрім використання плагіна Migrate для відновлення старої поведінки. Ви можете використовувати метод _.data () як згадування jps, але це внутрішній метод. Тому просто зробіть правильно, і використовуйте плагін Migrate, якщо вам потрібна ця функціональність.

З документації jQuery на .data("events")

До 1.9 .data ("події") можна було б використовувати для отримання недокументованої внутрішньої структури даних про jQuery для елемента, якщо жоден інший код не визначив елемент даних з назвою "події". Цей особливий випадок було вилучено в 1.9. Немає публічного інтерфейсу для отримання цієї внутрішньої структури даних, і він залишається недокументованим. Однак плагін jQuery Migrate відновлює цю поведінку для коду, який залежить від нього.


Прийнята відповідь також чітко показує новий, правильний спосіб отримати її для останніх версій: jQuery._data( elem, "events" );...
Ян

2
Приватний, недокументований спосіб ніколи не буде правильним . Правильний спосіб - означає документований, загальнодоступний та призначений - використовувати плагін Migrate.
олігофрен

3
Ви, здається, неправильно розумієте суть плагіна Migrate. jQuery видалив застарілі функції, а плагін Migrate - це допомогти перенести код розробника на новіші версії, щоб вони могли негайно скористатися новими можливостями та вдосконаленнями, але не втратити функціональність. Це покликане допомогти кодеру побачити, що їм потрібно зробити, щоб правильно почати використовувати нові версії jQuery. Не слід використовувати його у виробництві для відновлення функцій. Крім того, багато речей не зафіксовано і не оновлюється в документації jQuery - вони вже вказували на це раніше, тому це не є причиною
Іван

Крім того, якщо він включений як пропозиція в блозі jQuery, я б використовував його: blog.jquery.com/2012/08/09/jquery-1-8-вийшов
Ian

Ваші міркування про плагін Migrate здаються розумними. Гаразд, якщо я видалю свою відповідь?
олігофрен

5

Щоб перевірити наявність подій на елементі:

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

Зауважте, що це буде працювати лише з прямими обробниками подій, якщо ви використовуєте $ (document) .on ("ім'я події", "jq-селектор", функція () {// логіка}), ви хочете побачити 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 знову, але корпорація потребує цього ...). EDIT: Дані $ ._ містять повторювані події для елемента, хоча у 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)")

Це поверне елементи, до яких прикріплений обробник кліків.


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" невизначеного в <anonymous>: 1: 62
Mike 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 (document) .data ("події") дає мені невизначеність
Mike W

1

Маю сказати, що багато відповідей цікаві, але останнім часом у мене була подібна проблема, і рішення було надзвичайно простим шляхом проходження DOM шляхом. Він відрізняється тим, що ви не повторюєте, а орієнтуєтесь безпосередньо на потрібну вам подію, але нижче я дам більш загальну відповідь.

У мене було зображення підряд:

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

І до цього зображення було додано обробник подій клацання:

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

Мій намір полягав у тому, щоб розширити область для натискання на весь ряд, тому я спершу отримав усі зображення та відносні рядки:

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

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

Тепер у фактичній лінійці я просто зробив наступне, даючи відповідь на початкове запитання:

tr.click(this.onclick);

Тому я взяв обробник подій безпосередньо з елемента DOM і помістив його в обробник подій jQuery click. Працює як шарм.

Тепер до загальної справи. У старі часи до 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;

1
Я думаю, що jQuery робить оптимізацію обробки подій, яку, на мою думку, тут обійдений вашим кодом.
Еміль Бержерон

Дякую, так, у мене було відчуття, що це хакі - будь-яка гарна посилання, щоб дізнатися більше про оптимізацію?
темпранова

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

але це важка робота, оскільки є події для цілої веб-сторінки.

Том G вище створив функцію, яка фільтрує документ лише за подіями заданого елемента та об'єднує вихід обох методів, але у нього є недолік дублювання подій у висновку (і ефективно у внутрішньому списку подій 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.