调试时或从JavaScript代码中如何在DOM节点上查找事件侦听器?


840

我有一个页面,其中一些事件侦听器附加到输入框和选择框。有没有一种方法可以找出哪些事件侦听器正在观察特定的DOM节点以及什么事件?

使用以下事件附加事件:

  1. Prototype的 Event.observe ;
  2. DOM的addEventListener
  3. 作为元素属性element.onclick

3
首先如何附加事件?您是否正在使用库(例如Prototype,jQuery等)?

请务必注意,可以通过,为同一类型的事件附加多个回调函数element.addEventListener(type, callback, [bubble]),而element.onclick = function每次分配时它们都会被覆盖。
布雷登·贝斯特

Answers:


507

如果只需要检查页面上发生的事情,则可以尝试使用Visual Event书签。

更新Visual Event 2可用。


1
完善!击败Firebug的EventBug插件。
罗宾·马本

22
这会在页面上增加数十万个元素,其中许多是图像。在具有许多事件处理程序的页面上,它的有用性大大降低了(我的代码为17k,而Visual Event的加载时间约为3分钟)。
Tony R

15
我相信提到Chrome扩展@ssharma是一个可以在这里找到
kmote

1
如果页面引用了第三方js库,则Visial事件将不起作用。它将引发错误:XMLHttpRequest无法加载A.com/js/jquery-ui-1.10.3.custom.js?_=1384831682813。Access-Control-Allow-Origin不允许Origin B.com
13年

5
Chrome和FF内部开发人员工具(F12)具有内置的事件查看器!Chrome:在“元素”标签上,选择和元素,然后在右侧看到:样式,计算的,事件侦听器
oriadam 2015年

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.datajQuery.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
谢谢更新。不幸的是,您必须遍历每种类型的处理程序。
Keith Bentrup 09年

4
在“方法B”(的addEventListener)这里是关于枚举设施的状况与纯DOM API事件处理程序注册一个答案:stackoverflow.com/questions/7810534/...
Nickolay

@约翰:感谢您的评论。您有一个“真实JavaScript”示例来获取以前通过addEventListener?添加的侦听器。
Crescent Fresh

6
@Jan,这似乎不适用于jQuery 1.7.2,该版本是否有其他方法?
tomdemuyt 2012年

14
@tomdemuyt在jQuery中,事件现在存储在内部数据数组中,而不是.data('events')像以前那样可以通过访问。要访问内部事件数据,请使用$._data(elem, 'events')。请注意,此功能在jQuery源中标记为“仅供内部使用”,因此不保证将来会一直可用,但是我相信自jQuery 1.7起它就一直有效,并且仍然有效。
马特·布朗

350

getEventListeners(domElement)开发人员工具控制台中支持Chrome,Firefox,Vivaldi和Safari 。

对于大多数调试目的,可以使用它。

以下是使用它的很好参考:https : //developers.google.com/web/tools/chrome-devtools/console/utilities#geteventlisteners


1
+1。特定于浏览器的代码对我来说不是问题,因为我试图获取一个我无法控制的页面以使其正常工作。
Grault

9
不错,但仅在Chrome控制台的命令行中有效:(
gillyspy 2013年

5
@Raghav啊,谢谢,用法不是:我首先想到的是:)getEventListeners(object) obj getEventListeners()
jave.web

11
如果您已经解释了如何获取事件监听器的源代码,则可以为我节省3个小时。那个“参考”没有任何解释。如果其他人不知所措,请按以下步骤操作:var list = getEventListeners(document.getElementById("YOURIDHERE")); console.log(list["focus"][0]["listener"].toString())。如果同一事件上有多个侦听器,则将“焦点”更改为要检查的事件,并将其更改为数字。
doABarrelRoll721 '02

46
提示:getEventListeners($0)将获取您在Chrome开发工具中关注的元素的事件监听器。我经常用这个。爱不必使用querySelector或get__By_函数
cacoder

91

Chrome或Safari浏览器中的WebKit Inspector现在可以执行此操作。在“元素”窗格中选择它时,它将显示DOM元素的事件侦听器。


9
我不确定它是否显示所有事件处理程序。只是单个HTML事件处理程序。
huyz 2011年

3
我应该提到Firebug的EventBug插件以确保完整性< softwareishard.com/blog/category/eventbug >
Nickolay 2011年

这只是我的一天,一些遗留的原型代码在同一元素上具有绑定到同一事件的多个功能,而我一直在努力寻找它们。非常感谢Ishan。
Siliconrockstar 2013年

70

可以用JavaScript 列出所有事件侦听器:您只需修改prototypeHTML元素的方法(添加侦听器之前)。

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
如果要编写用户脚本或内容脚本,则此方法将不起作用。这些天不仅可能被沙盒化,而且如何保证执行顺序?
huyz

此方法适用于chrome / 19和FF / 12。如果您在其他脚本之前将其钩住,则可以确保执行顺序
Tzury Bar Yochay 2012年

8
您不能只修改它Node.prototype吗?那是从任何地方HTMLAnchorElement继承的地方.addEventListener。”
Esailija 2012年

3
您假设用户代理为DOM主机对象实现了原型继承,并允许您对其进行修改。这些都不是个好主意:不要修改您不拥有的对象
RobG 2012年

1
这几乎没有用-它显示了添加了哪些eventListener,在某些情况下可能会很好,但删除的内容却不是。因此,如果您要调试已删除的内容和未删除的内容,则根本没有办法。
gotofritz


41

(由于此问题很重要,因此请重写此问题的答案。)

调试时,如果您只想查看事件,我建议...

  1. 视觉事件
  2. Chrome开发人员工具的“ 元素”部分:选择一个元素并在右下角查找“事件监听器”(与Firefox类似)

如果要在代码中使用事件,并且正在使用1.8之前的 jQuery ,则可以使用:

$(selector).data("events")

得到事件。从1.8版本开始,不再使用.data(“ events”)(请参阅此错误通知单)。您可以使用:

$._data(element, "events")

另一个示例:在指向控制台的特定链接上写入所有click事件:

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

(有关工作示例,请参见http://jsfiddle.net/HmsQC/

不幸的是,除调试外,不建议使用$ ._ data,因为它是内部jQuery结构,并且在以后的版本中可能会更改。不幸的是,我知道没有其他简单的方法可以访问事件。


1
$._data(elem, "events")似乎不适用于jQuery 1.10,至少对于使用进行注册的事件而言$(elem).on('event', ...)。有人知道如何调试那些吗?
Marius Gedminas 2013年

4
我不是很肯定,但是我认为的第一个参数$._data可能需要一个元素而不是一个jQuery对象。所以我需要将我的示例修正为console.log($._data($myLink[0], "events").click);
Luke

29

1:Prototype.observe使用Element.addEventListener(请参见源代码

2:您可以重写Element.addEventListener以记住添加的侦听器(handy属性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.onclickcontent属性:这是两件事:

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的属性包含在3级DOM规范中,但不幸的是在以后的草案中被删除了。因此,我们不得不查看各个Javascript库,这些Java库通常维护附加事件的缓存(以便以后可以将其删除并执行其他有用的抽象)。

这样,为了让Visual Event显示事件,它必须能够从Javascript库中解析事件信息。

元素覆盖可能是有问题的(即,因为有一些DOM特定功能,例如实时集合,无法在JS中进行编码),但是它原生提供了eventListenerList支持,并且可以在Chrome,Firefox和Opera中使用(不适用于IE7) )。


23

您可以通过将本机DOM方法放在您的顶部来包装用于管理事件监听器的本机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 '02

@MaciejKrawczyk是的,那些冒泡的人
Jon z

18

Firefox开发人员工具现在可以执行此操作。通过单击每个元素显示右侧的“ ev”按钮来显示事件,包括jQueryDOM事件。

检查器选项卡中Firefox开发人员工具的事件侦听器按钮的屏幕截图


我确实查看了Visual Event 2小书签正在显示连接到的事件的页面上的dom元素之一,并且确实看到了右侧的小“ ev”按钮,如您所示。
埃里克

Firefox的事件侦听器可以找出jQuery委托的事件,而Chrome需要插件来完成。


10

完全有效的解决方案基于Jan Turon的回答 -行为类似于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
这是一个不错的答案,但我无法在bodyand document元素上使用它。
戴维·布拉德肖

真的很好的解决方案!
Dzhakhar Ukhaev '01

@Peter Mortensen,您为什么更改Jan的名字?:)
n00b

1
@David Bradshaw:因为主体,文档和窗口具有自己的原型,而不是Element原型...
Stefan Steiger

1
@ n00b:有一个错误。这就是为什么嗯。您可以在if语句中为侦听器和useCaption使用逗号运算符...逗号运算符将评估其每个操作数(从左到右)并返回最后一个操作数的值。因此,无论事件的类型如何,您都删除与useCapture匹配的第一个eventListener……应该是&&而不是逗号。
Stefan Steiger,

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对象来保存事件的必要信息,即:targettypecallbackoptionsuseCapturewantsUntrusted,和添加的方法remove(),以除去该侦听器。

最后,我扩展了native addEventListener()removeEventListener()方法,使它们可以与我创建的对象(EventListenerEventListeners)一起使用。

用法:

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

// bodyClickEvent.remove();

addEventListener()创建一个EventListener对象,将其添加到EventListenersEventListener对象并返回该对象,以便以后可以将其删除。

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"));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.