Як знайти слухачів подій на вузлі DOM при налагодженні або з коду JavaScript?


840

У мене є сторінка, де деякі слухачі подій приєднуються до полів введення та вибирають поля. Чи є спосіб дізнатися, які слухачі подій спостерігають за певним вузлом DOM та за якою подією?

Події додаються за допомогою:

  1. Прототипи Event.observe ;
  2. DOM addEventListener;
  3. Як атрибут елемента element.onclick.

3
Як в першу чергу додаються події? Використовуєте бібліотеку (наприклад, прототип, jQuery тощо)?
Півмісяць свіжий

Важливо зауважити, що кілька функцій зворотного дзвінка можуть бути додані для одного типу подій через element.addEventListener(type, callback, [bubble]), при цьому вони element.onclick = functionбудуть перезаписані щоразу, коли ви призначаєте.
Бреден Кращий

Відповіді:


507

Якщо вам просто потрібно перевірити, що відбувається на сторінці, ви можете спробувати закладку Visual Event .

Оновлення : Візуальна подія 2 доступна.


1
Ідеально! Бій EventBugплагін Firebug .
Робін Мабен

22
Це додає сторінки мільйона елементів, багато з яких - зображення. Його корисність значно знижується на сторінці з багатьма обробниками подій (у мене 17 к., А завантаження Visual Event займало близько 3 хвилин).
Тоні Р

15
Я вважаю, що доступне тут
kmote

1
Visual Event не працює, якщо сторінка посилається на бібліотеку js сторонніх розробників. Це викликає помилку: XMLHttpRequest не може завантажити A.com/js/jquery-ui-1.10.3.custom.js?_=1384831682813 . Origin B.com заборонено системою Access-Control-Allow-Origin.
hiway

5
У внутрішніх інструментах для розробників Chrome і FF (F12) є вбудований переглядач подій! Chrome: на вкладці "Елементи" виберіть і вкажіть елемент і перегляньте праворуч: Стиль, Обчислювачі, Слухачі подій
oriadam

365

Це залежить від того, як додаються події. Для ілюстрації припущення, ми маємо наступний обробник кліків:

var handler = function() { alert('clicked!') };

Ми будемо прикріплювати його до нашого елемента за допомогою різних методів, деяких, які дозволяють перевірити, а інших - не.

Метод A) обробник події однієї події

element.onclick = handler;
// inspect
alert(element.onclick); // alerts "function() { alert('clicked!') }"

Метод B) кілька обробників подій

if(element.addEventListener) { // DOM standard
    element.addEventListener('click', handler, false)
} else if(element.attachEvent) { // IE
    element.attachEvent('onclick', handler)
}
// cannot inspect element to find handlers

Спосіб C): jQuery

$(element).click(handler);
  • 1.3.x

    // inspect
    var clickEvents = $(element).data("events").click;
    jQuery.each(clickEvents, function(key, value) {
        alert(value) // alerts "function() { alert('clicked!') }"
    })
  • 1.4.x (зберігає обробник всередині об'єкта)

    // inspect
    var clickEvents = $(element).data("events").click;
    jQuery.each(clickEvents, function(key, handlerObj) {
        alert(handlerObj.handler) // alerts "function() { alert('clicked!') }"
        // also available: handlerObj.type, handlerObj.namespace
    })

(Див. jQuery.fn.dataІ jQuery.data)

Спосіб D): прототип (безладний)

$(element).observe('click', handler);
  • 1.5.x

    // inspect
    Event.observers.each(function(item) {
        if(item[0] == element) {
            alert(item[2]) // alerts "function() { alert('clicked!') }"
        }
    })
  • 1.6 до 1.6.0.3, включно (тут дуже важко)

    // inspect. "_eventId" is for < 1.6.0.3 while 
    // "_prototypeEventID" was introduced in 1.6.0.3
    var clickEvents = Event.cache[element._eventId || (element._prototypeEventID || [])[0]].click;
    clickEvents.each(function(wrapper){
        alert(wrapper.handler) // alerts "function() { alert('clicked!') }"
    })
  • 1.6.1 (трохи краще)

    // inspect
    var clickEvents = element.getStorage().get('prototype_event_registry').get('click');
    clickEvents.each(function(wrapper){
        alert(wrapper.handler) // alerts "function() { alert('clicked!') }"
    })

3
Thx для оновлення цього. Прикро, що вам доведеться повторити через кожен тип обробників.
Кіт Бентруп

4
На "Метод B" (addEventListener) ось відповідь щодо стану засобів перерахування для обробників, зареєстрованих із чистим API DOM подій: stackoverflow.com/questions/7810534/…
Nickolay

@John: дякую за коментар. У вас є приклад "реального JavaScript" для отримання слухачів, доданих раніше addEventListener?
Півмісяць свіжий

6
@Jan, схоже, це не працює для jQuery 1.7.2, чи є інший підхід для цієї версії?
tomdemuyt

14
@tomdemuyt У jQuery події тепер зберігаються у внутрішньому масиві даних, а не доступні через те, .data('events')як були раніше. Щоб отримати доступ до внутрішніх даних про події, використовуйте $._data(elem, 'events'). Зауважте, що ця функція позначена "лише для внутрішнього використання" у джерелі jQuery, тому не обіцяє, що вона завжди працюватиме в майбутньому, але я вважаю, що вона працює з jQuery 1.7 і досі працює.
Метт Браун

350

Підтримка Chrome, Firefox, Vivaldi та Safari getEventListeners(domElement)на консолі Інструментів для розробників.

Для більшості налагоджувальних цілей це можна використовувати.

Нижче наведено дуже гарне посилання на його використання: https://developers.google.com/web/tools/chrome-devtools/console/utilities#geteventlisteners


1
+1. Специфічний для браузера код не є проблемою для мене, тому що я намагаюся отримати сторінку, яку я не контролюю, щоб працювати належним чином.
Граул

9
приємно, але працює лише в командному рядку консолі Chrome :(
gillyspy

5
@Raghav ах спасибі, використання НЕ: як я вперше подумав :)getEventListeners(object) obj getEventListeners()
jave.web

11
Ви могли врятувати мене три години, якби ви пояснили, як отримати вихідний код учасника події. Та "довідка" нічого не пояснила. У разі , якщо хто - то був на втрати ось як: var list = getEventListeners(document.getElementById("YOURIDHERE")); console.log(list["focus"][0]["listener"].toString()). Змініть "фокус" на подію, яку ви хочете перевірити, і кількість, якщо на одній події є кілька слухачів.
doABarrelRoll721

46
ПОРАДА: getEventListeners($0)отримає слухачів подій елемент, на який ви зосередилися в інструментах розробки Chrome. Я цим постійно користуюся. Любіть не використовувати querySelector або get__By_ функцій
cacoder

91

WebKit Inspector в браузерах Chrome або Safari зараз робить це. Він буде відображати слухачів подій для елемента DOM, коли ви вибираєте його в області елементів.


9
Я не впевнений, що це показує всі обробники подій; лише один обробник подій HTML.
хуйз

3
Я повинен згадати плагін EventBug для Firebug для повноти < softwareishard.com/blog/category/eventbug >
Nickolay

Це щойно зробило мені день, колись застарілий код прототипу мав декілька функцій, пов'язаних з однією і тією ж подією на одному елементі, і я вмирав, намагаючись відстежити їх. Дякую тобі, Ішан.
siliconrockstar

70

Можна перелічити всіх слухачів подій у JavaScript: Це не так складно; Вам просто потрібно зламати prototypeметод елементів HTML ( перед тим, як додати слухачів).

function reportIn(e){
    var a = this.lastListenerInfo[this.lastListenerInfo.length-1];
    console.log(a)
}


HTMLAnchorElement.prototype.realAddEventListener = HTMLAnchorElement.prototype.addEventListener;

HTMLAnchorElement.prototype.addEventListener = function(a,b,c){
    this.realAddEventListener(a,reportIn,c); 
    this.realAddEventListener(a,b,c); 
    if(!this.lastListenerInfo){  this.lastListenerInfo = new Array()};
    this.lastListenerInfo.push({a : a, b : b , c : c});
};

Тепер кожен елемент прив'язки ( a) матиме lastListenerInfoвластивість, яка містить усіх його слухачів. І це навіть працює для видалення слухачів з анонімними функціями.


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

цей метод працює з chrome / 19 та FF / 12. порядок виконання можна гарантувати, якщо зачепити це перед іншими сценаріями
Tzury Bar Yochay

8
Не могли ви просто змінити Node.prototypeнатомість? Ось де і HTMLAnchorElementспадіє .addEventListenerвід будь-якого місця ».
Есаїлія

3
Ви припускаєте, що агент користувача реалізує успадкування прототипу для хостів DOM та дозволяє вам їх змінювати. Жодна з цих гарних ідей: не змінюйте об'єкти, якими ви не володієте .
RobG

1
Це досить марно - це показує, які додані слухачі подій, що може бути корисним у деяких випадках, але не те, що було видалено. Тож якщо ви намагаєтеся налагодити те, що було видалено, а що ні, просто немає можливості.
gotofritz

52

Використовуйте getEventListeners в Google Chrome :

getEventListeners(document.getElementByID('btnlogin'));
getEventListeners($('#btnlogin'));

1
Uncaught ReferenceError: getEventListeners не визначено
Брайан Грейс

3
getEventListeners працює лише в консолі Devtools Chrome. І це дублікат цієї більш детальної відповіді: stackoverflow.com/a/16544813/1026
Ніколай

41

(Переписування відповіді з цього питання, оскільки воно тут актуальне.)

Під час налагодження, якщо ви просто хочете переглянути події, я рекомендую або ...

  1. Візуальна подія
  2. Розділ " Елементи " в Інструментах для розробників Chrome: виберіть елемент і знайдіть "Слухачі подій" в нижньому правому куті (подібне в Firefox)

Якщо ви хочете використовувати події у своєму коді, а ви використовуєте jQuery до версії 1.8 , ви можете використовувати:

$(selector).data("events")

щоб отримати події. З версії 1.8 використання .data ("події") припинено (див. Цей квиток на помилку ). Ви можете використовувати:

$._data(element, "events")

Інший приклад: запишіть усі події клацання на певному посиланні на консоль:

var $myLink = $('a.myClass');
console.log($._data($myLink[0], "events").click);

(див. http://jsfiddle.net/HmsQC/ для робочого прикладу)

На жаль, використання даних $ ._ це не рекомендується, за винятком налагодження, оскільки це внутрішня структура jQuery і може змінитись у майбутніх випусках. На жаль, я не знаю жодного іншого легкого способу доступу до подій.


1
$._data(elem, "events")не працює з jQuery 1.10, принаймні для подій, зареєстрованих за допомогою $(elem).on('event', ...). Хтось знає, як налагодити їх?
Маріус Гедмінас

4
Я не позитивний, але я думаю, що перший парам $._dataможе бути необхідним як елемент, а не об'єкт jQuery. Тому мені потрібно виправити свій приклад вищеconsole.log($._data($myLink[0], "events").click);
Лука

29

1: Prototype.observeвикористовує Element.addEventListener (див . Вихідний код )

2: Ви можете замінити, Element.addEventListenerщоб запам'ятати доданих слухачів (зручна властивість EventListenerListбула видалена з пропозиції специфікації DOM3). Запустіть цей код, перш ніж будь-яка подія додається:

(function() {
  Element.prototype._addEventListener = Element.prototype.addEventListener;
  Element.prototype.addEventListener = function(a,b,c) {
    this._addEventListener(a,b,c);
    if(!this.eventListenerList) this.eventListenerList = {};
    if(!this.eventListenerList[a]) this.eventListenerList[a] = [];
    this.eventListenerList[a].push(b);
  };
})();

Прочитайте всі події:

var clicks = someElement.eventListenerList.click;
if(clicks) clicks.forEach(function(f) {
  alert("I listen to this function: "+f.toString());
});

І не забудьте змінити, Element.removeEventListenerщоб видалити подію зі звичаю Element.eventListenerList.

3: Element.onclickмайно потребує особливого догляду тут:

if(someElement.onclick)
  alert("I also listen tho this: "+someElement.onclick.toString());

4: не забудьте Element.onclickатрибут вмісту: це дві різні речі:

someElement.onclick = someHandler; // IDL attribute
someElement.setAttribute("onclick","otherHandler(event)"); // content attribute

Тож вам теж потрібно впоратися:

var click = someElement.getAttribute("onclick");
if(click) alert("I even listen to this: "+click);

Закладка Visual Event (згадана у найпопулярнішій відповіді) викрадає лише кеш користувальницької бібліотеки:

Виявляється, не існує стандартного методу, що надається рекомендованим W3C інтерфейсом DOM, щоб з’ясувати, які слухачі подій приєднані до певного елемента. Хоча це може здатися недоглядом, була пропозиція включити властивість під назвою eventListenerList до специфікації DOM рівня 3, але, на жаль, було видалено в наступних проектах. Як таких ми змушені розглядати окремі бібліотеки Javascript, які зазвичай підтримують кеш доданих подій (щоб потім їх можна було видалити та виконати інші корисні абстракції).

Таким чином, для того, щоб Visual Event відображав події, він повинен мати можливість аналізувати інформацію про подію з бібліотеки Javascript.

Перевизначення елементів може бути сумнівним (тобто тому, що є деякі особливості DOM, такі як живі колекції, які неможливо кодувати в JS), але він дає підтримку eventListenerList спочатку і працює в Chrome, Firefox та Opera (не працює в IE7 ).


23

Ви можете обернути рідні методи DOM для управління слухачами подій, поставивши це у верхній частині свого <head>:

<script>
    (function(w){
        var originalAdd = w.addEventListener;
        w.addEventListener = function(){
            // add your own stuff here to debug
            return originalAdd.apply(this, arguments);
        };

        var originalRemove = w.removeEventListener;
        w.removeEventListener = function(){
            // add your own stuff here to debug
            return originalRemove.apply(this, arguments);
        };
    })(window);
</script>

H / T @ les2


Виправте мене, якщо я помиляюся, але хіба це не лише для слухачів подій, прикріплених до вікна?
Maciej Krawczyk

@MaciejKrawczyk так, і ті, що бульбашки
Jon z

18

Інструменти для розробників Firefox зараз це роблять. Події відображаються натисканням кнопки "ev" праворуч на екрані кожного елемента, включаючи події jQuery та DOM .

Знімок екрана слухача подій інструментів для розробників Firefox на вкладці інспектора


Я переглянув один з елементів дому на сторінці, на якому в закладці Visual Event 2 відображається подія, підключений, і я побачив маленьку кнопку "ev" праворуч, як ви показуєте.
Ерік

Слухач подій Firefox може виявити делеговані події jQuery, а для цього Chrome потрібен плагін.
Нік Цай

12

Якщо у вас є Firebug , ви можете використовувати console.dir(object or array)для друку приємного дерева в журналі консолі будь-якого скалярного масиву, масиву чи об’єкта JavaScript.

Спробуйте:

console.dir(clickEvents);

або

console.dir(window);


10

Повністю працює рішення, засноване на відповіді Яна Турона - поводиться як getEventListeners()з консолі:

(Існує невелика помилка з дублікатами. Так чи інакше вона не зламається.)

(function() {
  Element.prototype._addEventListener = Element.prototype.addEventListener;
  Element.prototype.addEventListener = function(a,b,c) {
    if(c==undefined)
      c=false;
    this._addEventListener(a,b,c);
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(!this.eventListenerList[a])
      this.eventListenerList[a] = [];
    //this.removeEventListener(a,b,c); // TODO - handle duplicates..
    this.eventListenerList[a].push({listener:b,useCapture:c});
  };

  Element.prototype.getEventListeners = function(a){
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(a==undefined)
      return this.eventListenerList;
    return this.eventListenerList[a];
  };
  Element.prototype.clearEventListeners = function(a){
    if(!this.eventListenerList)
      this.eventListenerList = {};
    if(a==undefined){
      for(var x in (this.getEventListeners())) this.clearEventListeners(x);
        return;
    }
    var el = this.getEventListeners(a);
    if(el==undefined)
      return;
    for(var i = el.length - 1; i >= 0; --i) {
      var ev = el[i];
      this.removeEventListener(a, ev.listener, ev.useCapture);
    }
  };

  Element.prototype._removeEventListener = Element.prototype.removeEventListener;
  Element.prototype.removeEventListener = function(a,b,c) {
    if(c==undefined)
      c=false;
    this._removeEventListener(a,b,c);
      if(!this.eventListenerList)
        this.eventListenerList = {};
      if(!this.eventListenerList[a])
        this.eventListenerList[a] = [];

      // Find the event in the list
      for(var i=0;i<this.eventListenerList[a].length;i++){
          if(this.eventListenerList[a][i].listener==b, this.eventListenerList[a][i].useCapture==c){ // Hmm..
              this.eventListenerList[a].splice(i, 1);
              break;
          }
      }
    if(this.eventListenerList[a].length==0)
      delete this.eventListenerList[a];
  };
})();

Використання:

someElement.getEventListeners([name]) - список повернень слухачів подій, якщо ім'ям встановлено масив повернення слухачів для цієї події

someElement.clearEventListeners([name]) - видалити всіх слухачів подій, якщо ім'я встановлено лише видалити слухачів для цієї події


3
Це гарна відповідь, але я не можу змусити його працювати над bodyіdocument елементами елементами.
Девід Бредшоу

Дійсно приємне рішення!
Джахар Ухаєв

@ Петер Мортенсен, чому ти змінив ім'я Яна? :)
n00b

1
@David Bradshaw: тому що корпус, документ і вікно мають власний прототип, а не прототип Element ...
Стефан Штайгер

1
@ n00b: помилка. Ось чому хммм. Ви використовуєте оператор кома в операторі if для слухача і useCaption ... Оператор кома оцінює кожен з його операндів (зліва направо) і повертає значення останнього операнду. Таким чином, ви видаляєте перший EventListener, який відповідає в useCapture, незалежно від типу події ... Це має бути && замість коми.
Стефан Штайгер

8

Opera 12 (не найновіший движок Chrome Webkit) Dragonfly мав це протягом певного часу і очевидно відображається в структурі DOM На мою думку, це вищий налагоджувач і є єдиною причиною, чому я все ще використовую версію на базі Opera 12 (немає версії v13, v14, а в V15 Webkit на базі ще немає Dragonfly)

введіть тут опис зображення


4

Прототип 1.7.1 способом

function get_element_registry(element) {
    var cache = Event.cache;
    if(element === window) return 0;
    if(typeof element._prototypeUID === 'undefined') {
        element._prototypeUID = Element.Storage.UID++;
    }
    var uid =  element._prototypeUID;           
    if(!cache[uid]) cache[uid] = {element: element};
    return cache[uid];
}

4

Я нещодавно працював з подіями і хотів переглянути / контролювати всі події на сторінці. Переглянувши можливі рішення, я вирішив піти власним шляхом і створити власну систему для моніторингу подій. Отже, я зробив три речі.

По-перше, мені знадобився контейнер для всіх слухачів подій на сторінці: це EventListenersоб’єкт. Вона складається з трьох корисних методів: add(), remove(), і get().

Потім я створив EventListenerоб'єкт для зберігання необхідної інформації для події, тобто target, type, callback, options, useCapture,wantsUntrusted , і додав метод remove()для видалення слухача.

Нарешті, я розширив нативні addEventListener()і removeEventListener()методи, щоб змусити їх працювати з створеними мною об'єктами ( EventListenerіEventListeners ) .

Використання:

var bodyClickEvent = document.body.addEventListener("click", function () {
    console.log("body click");
});

// bodyClickEvent.remove();

addEventListener()створює EventListenerоб’єкт, додає його EventListenersі повертає EventListenerоб'єкт, щоб його можна було потім видалити.

EventListeners.get()можна використовувати для перегляду слухачів на сторінці. Він приймає EventTargetабо рядок (тип події).

// EventListeners.get(document.body);
// EventListeners.get("click");

Демо

Скажімо, ми хочемо знати кожного слухача події на цій поточній сторінці. Ми можемо це зробити (припустимо, що ви використовуєте розширення менеджера сценаріїв, у цьому випадку Tampermonkey). Наступний сценарій робить це:

// ==UserScript==
// @name         New Userscript
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @include      https://stackoverflow.com/*
// @grant        none
// ==/UserScript==

(function() {
    fetch("https://raw.githubusercontent.com/akinuri/js-lib/master/EventListener.js")
        .then(function (response) {
            return response.text();
        })
        .then(function (text) {
            eval(text);
            window.EventListeners = EventListeners;
        });
})(window);

І коли ми перераховуємо всіх слухачів, він говорить, що є 299 слухачів подій. Там, здається, є кілька дублікатів, але я не знаю, чи справді це дублікати. Не кожен тип події дублюється, тому всі ці "дублікати" можуть бути окремими слухачами.

скріншот консолі з переліком усіх слухачів подій на цій сторінці

Код можна знайти в моєму сховищі. Я не хотів публікувати його тут, тому що це досить довго.


Оновлення: Схоже, це не працює з jQuery. Переглядаючи EventListener, я бачу, що зворотний виклик є

function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}

Я вважаю, що це належить до jQuery, а не є фактичним зворотним дзвоном. jQuery зберігає фактичний зворотний виклик у властивостях EventTarget:

$(document.body).click(function () {
    console.log("jquery click");
});

введіть тут опис зображення

Щоб видалити слухача подій, фактичний зворотний виклик потрібно передати removeEventListener()методу. Отже, щоб зробити цю роботу з jQuery, вона потребує подальшої модифікації. Я можу це виправити в майбутньому.


Чудово працює! Спасибі
mt025

3

Я намагаюся зробити це в jQuery 2.1 та з "$().click() -> $(element).data("events").click; методом " це не працює.

Я зрозумів, що в моєму випадку працює лише функція $ ._ data ():

	$(document).ready(function(){

		var node = $('body');
		
        // Bind 3 events to body click
		node.click(function(e) { alert('hello');  })
			.click(function(e) { alert('bye');  })
			.click(fun_1);

        // Inspect the events of body
		var events = $._data(node[0], "events").click;
		var ev1 = events[0].handler // -> function(e) { alert('hello')
		var ev2 = events[1].handler // -> function(e) { alert('bye')
		var ev3 = events[2].handler // -> function fun_1()
        
		$('body')
			.append('<p> Event1 = ' + eval(ev1).toString() + '</p>')
			.append('<p> Event2 = ' + eval(ev2).toString() + '</p>')
			.append('<p> Event3 = ' + eval(ev3).toString() + '</p>');        
	
	});

	function fun_1() {
		var txt = 'text del missatge';	 
		alert(txt);
	}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
</body>



1

зміна цих функцій дозволить вам записати доданих слухачів:

EventTarget.prototype.addEventListener
EventTarget.prototype.attachEvent
EventTarget.prototype.removeEventListener
EventTarget.prototype.detachEvent

читати решту слухачів с

console.log(someElement.onclick);
console.log(someElement.getAttribute("onclick"));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.