Как найти прослушиватели событий на узле DOM при отладке или из кода JavaScript?

У меня есть страница, где некоторые слушатели событий прикреплены к полям ввода и полям выбора. Есть ли способ узнать, какие слушатели событий наблюдают за конкретным узлом DOM и для какого события?

События прикрепляются с использованием:

  1. прототипа Event.observe;
  2. DOM-х addEventListener;
  3. Как атрибут элемента element.onclick,

21 ответ

Решение

Если вам просто нужно проверить, что происходит на странице, вы можете попробовать букмарклет Visual Event.

Обновление: доступно визуальное событие 2;

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

Для большинства целей отладки это можно использовать.

Ниже очень хорошая ссылка для его использования: https://developers.google.com/chrome-developer-tools/docs/commandline-api

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

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

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

Метод А) один обработчик события

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

Метод Б) несколько обработчиков событий

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!') }"
    })
    

Инспектор WebKit в браузерах Chrome или Safari теперь делает это. Он отобразит прослушиватели событий для элемента DOM, когда вы выберете его на панели элементов.

Можно перечислить всех слушателей событий в 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 свойство, которое содержит всех своих слушателей. И это даже работает для удаления слушателей с анонимными функциями.

Используйте getEventListeners в Google Chrome:

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

(Переписать ответ на этот вопрос, поскольку он уместен здесь.)

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

  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: 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, которые обычно поддерживают кэш прикрепленных событий (чтобы впоследствии их можно было удалить и выполнить другие полезные абстракции).

Таким образом, чтобы визуальное событие отображало события, оно должно иметь возможность анализировать информацию о событиях из библиотеки Javascript.

Переопределение элементов может быть сомнительным (т. Е. Потому, что есть некоторые специфические функции DOM, такие как живые коллекции, которые не могут быть закодированы в JS), но это обеспечивает встроенную поддержку eventListenerList и работает в Chrome, Firefox и Opera (не работает в IE7).

Чтобы все слушатели событий на странице распечатывались вместе с их элементами

Array.from(document.querySelectorAll("*")).forEach(e => {
    const ev = getEventListeners(e)
    if (Object.keys(ev).length !== 0) console.log(e, ev)
})

Вы можете обернуть нативные методы 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

Инструменты разработчика Firefox теперь делают это. События отображаются нажатием кнопки "ev" справа от каждого элемента, включая события jQuery и DOM.

Снимок экрана: кнопка прослушивания событий инструментов разработчика Firefox на вкладке инспектора

Обновление 2022:

вChrome Developer Tools, вElements panel, ЗдесьEvent Listenersвкладку, где вы можете увидеть слушателей для элемента.

Вы также можете снять флажок «Предки», чтобы отображались только слушатели для этого элемента.

Полностью рабочее решение, основанное на ответе Яна Турона - ведет себя как 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]) - удалить все прослушиватели событий, если установлено имя, удалить только прослушиватели для этого события

Если у вас есть Firebug, вы можете использовать console.dir(object or array) напечатать красивое дерево в журнале консоли любого скаляра JavaScript, массива или объекта.

Пытаться:

console.dir(clickEvents);

или же

console.dir(window);

Opera 12 (не последняя версия на основе движка Chrome Webkit) Dragonfly некоторое время имела это и, очевидно, отображается в структуре DOM. На мой взгляд, это превосходный отладчик, и это единственная причина, почему я до сих пор использую версию для Opera 12 (версии v13, v14 нет, а в версии 15 для Webkit по-прежнему отсутствует Dragonfly)

Прототип 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];
}

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

Во-первых, мне нужен контейнер для всех слушателей событий на странице: это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://stackru.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, он нуждается в дальнейшей модификации. Я мог бы исправить это в будущем.

Я пытаюсь сделать это в 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>

Существует хорошее расширение jQuery Events:

( источник темы)

В настоящее время, сентябрь 2023 г., в браузере Firefox 117.0.1 вы можете увидеть это, нажав соответствующую кнопку « событие » в «Инспекторе» в «Инструментах разработчика», как показано ниже:

Изменение этих функций позволит вам регистрировать добавленные слушатели:

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

читать остальных слушателей с

console.log(someElement.onclick);
console.log(someElement.getAttribute("onclick"));
Другие вопросы по тегам