首页 文章

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

提问于
浏览
727

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

事件附件使用:

  • Prototype's Event.observe ;

  • DOM的 addEventListener ;

  • 作为元素属性 element.onclick .

17 回答

  • 38

    我最近在处理事件,想要查看/控制页面中的所有事件 . 看了可能的解决方案之后,我决定采用自己的方式创建一个自定义系统来监控事件 . 所以,我做了三件事 .

    首先,我需要一个容器用于页面中的所有事件监听器:这是 EventListeners 对象 . 它有三个有用的方法: add()remove()get() .

    接下来,我创建了一个 EventListener 对象来保存事件的必要信息,即: targettypecallbackoptionsuseCapturewantsUntrusted ,并添加了一个方法 remove() 来删除侦听器 .

    最后,我扩展了原生 addEventListener()removeEventListener() 方法,使它们可以处理我创建的对象( EventListenerEventListeners ) .

    用法:

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

    Demo

    假设我们想知道当前页面中的每个事件监听器 . 我们可以这样做(假设你使用脚本管理器扩展,在这种情况下是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个事件监听器 . “似乎”有些重复,但我不知道它们是否真的重复 . 并非每个事件类型都是重复的,因此所有这些“重复”都可能是一个单独的监听器 .

    代码可以在我的repository.找到,我没有't want to post it here because it' s相当长 .


    Update: 这似乎不适用于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,它需要进一步修改 . 我可能会在将来解决这个问题 .

  • 7

    (重写this question的答案,因为它与此相关 . )

    调试时,如果你只是想看事件,我建议......

    • Visual Event

    • Chrome开发人员工具的“元素”部分:选择一个元素并在右下角查找"Event Listeners"(在Firefox中类似)

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

    $(selector).data("events")
    

    得到事件 . As of version 1.8, using .data("events") is discontinued (见this bug ticket) . 您可以使用:

    $._data(element, "events")
    

    另一个示例:将特定链接上的所有点击事件写入控制台:

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

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

    不幸的是, using $._data this is not recommended 除了调试,因为它是一个内部jQuery结构,并且可能在将来的版本中发生变化 . 不幸的是,我知道没有其他简单的方法可以访问这些事件 .

  • 18

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

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

    以下是使用它的非常好的参考:https://developers.google.com/chrome-developer-tools/docs/commandline-api#geteventlistenersobject

  • 2

    我试图在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>
    
  • 0

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

  • 34

    1: Prototype.observe 使用Element.addEventListener(参见the source code

    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 bookmarklet(在最流行的答案中提到)只窃取自定义库处理程序缓存:

    事实证明,W3C推荐的DOM接口没有提供标准方法来找出哪些事件监听器附加到特定元素 . 虽然这似乎是一种疏忽,但有人建议将一个名为eventListenerList的属性包含在3级DOM规范中,但不幸的是在以后的草稿中已将其删除 . 因此,我们不得不查看单个Javascript库,这些库通常维护附加事件的缓存(以便以后可以删除它们并执行其他有用的抽象) . 因此,为了使Visual Event显示事件,它必须能够解析事件信息Javascript库 .

    元素重写可能有问题(即因为有一些DOM特定功能,如实时集合,无法在JS中编码),但它本身提供了eventListenerList支持,它可以在Chrome,Firefox和Opera中运行(在IE7中不起作用) ) .

  • 21

    这取决于事件的附加方式 . 为了说明,我们有以下点击处理程序:

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

    基于answer by 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]) - 返回事件侦听器列表,如果设置了name,则返回该事件的侦听器数组

    someElement.clearEventListeners([name]) - 删除所有事件侦听器,如果设置了name,则只删除该事件的侦听器

  • 64

    Google Chrome 中使用getEventListeners:

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

    原型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];
    }
    
  • 348

    Firefox开发人员工具现在就这样做了 . 通过单击每个元素显示右侧的"ev"按钮显示事件,包括jQueryDOM事件 .

    Screenshot of Firefox developer tools' event listener button in the inspector tab

  • 2

    如果你有Firebug,你可以使用 console.dir(object or array) 在任何JavaScript标量,数组或对象的控制台日志中打印一个漂亮的树 .

    尝试:

    console.dir(clickEvents);
    

    要么

    console.dir(window);
    
  • 12

    It is possible to list all event listeners 在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 属性 . 它甚至可以用于删除具有匿名功能的侦听器 .

  • 472

    Opera 12(不是最新的Chrome Webkit引擎)Dragonfly已经有一段时间了,显然在DOM结构中显示 . 在我看来,它是一个优秀的调试器,是我仍然使用基于Opera 12的版本的唯一原因(没有v13,v14版本和基于v15 Webkit仍然缺乏Dragonfly)

    enter image description here

  • 25

    有好 jQuery Events extension

    (主题source

  • 4

    如果您只需要检查页面上发生的情况,可以尝试使用Visual Event bookmarklet .

    UpdateVisual Event 2可用;

  • 7

    您可以通过将其放在 <head> 的顶部来包装用于管理事件侦听器的本机DOM方法:

    <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

相关问题