使用jQuery查找注册在对象上的事件处理程序

600

我需要找出一个对象上注册的事件处理程序。

例如:

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

$("#el")已注册了clickmouseover事件。

有没有一种方法可以查找它们,并可能迭代事件处理程序?

如果无法通过适当的方法在jQuery对象上实现,那么在普通DOM对象上是否可行?


5
不幸的是,现在出现了这个问题:http://bugs.jquery.com/ticket/10589 - Skylar Saveland
2
支持 jQuery 1.8 前后版本:var events = (jQuery._data || jQuery.data)(elem, 'events'); - oriadam
3
请注意,您可以使用FF和Chrome开发工具(F12)来查看这些事件侦听器。请参阅https://developers.google.com/web/tools/chrome-devtools/debug/command-line/events#view-event-listeners-registered-on-dom-elements和https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_event_listeners - oriadam
16个回答

735

从jQuery 1.8开始,事件数据不再在“公共API”中提供。请阅读jQuery博客文章。现在应该使用以下内容:

jQuery._data( elem, "events" );

elem 应该是一个 HTML 元素,而不是 jQuery 对象或选择器。

请注意,这是一种内部的“私有”结构,不应进行修改。仅用于调试目的。

在旧版本的 jQuery 中,你可能需要使用旧的方法:

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

247
但你仍然可以使用 $._data($(elem).get(0), "events") - bullgare
10
jQuery将其与事件相关的数据存储在名为“events”的数据对象中,这是每个元素上的一个内部数据结构。在1.8版本中,为了防止与相同名称的项目冲突,这将从用户数据命名空间中删除。jQuery的事件数据仍然可以通过jQuery._data(element, "events")访问,但请注意这是一个未记录的内部数据结构,不应进行修改。 - Sam Greenhalgh
2
无缝支持两个选项:var events = (jQuery._data || jQuery.data)(elem, 'events'); - oriadam
1
@CrazyMerlin 好的,谢谢你。现在也许你有更好的答案? - enorl76
3
它正在返回未定义。 - Rajat
显示剩余6条评论

94

你可以通过爬取事件(自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
可以在1.4版本中使用,但无法在jQuery 1.8.2中使用。 - Timo Kähkönen
18
对于jQuery 1.8+,你必须使用“私有数据”方法:jQuery._data(jQuery("#el")[0], "events");而不是“公共数据”方法:jQuery("#el").data("events")。实际上已经很长时间没有将events对象存储在.data()中了,我们通过从“公共API”中删除此“代理”来删除了一些字节的代码。 - gnarf

37

对于 jQuery 1.8+ 版本,由于内部数据被放置在不同的对象中,原来的方法将不再起作用。

现在最新的非官方方法(但在以前的版本中也有效,至少在 1.7.2 版本中有效)是 - $._data(element, "events")

这里下划线("_")很关键。在内部,它调用了 $.data(element, name, null, true),最后(第四个)参数是内部参数(“pvt”)。


1
$._data("body", "events") 未定义 $().jquery; "1.7.1"(尝试了1.7.2和1.8.1,始终返回"未定义") - Mars Robertson
2
@Michal - http://api.jquery.com/jQuery.data/ 表示它接受一个元素,而不是选择器。 - PhistucK
1
现在运行良好:$._data($("body").get(0), "events") 或者更好的是:$("body").data("events")! - Mars Robertson
2
FWIW - 指出它“内部”使用参数调用其他数据函数,我们特别不需要记录可能是不必要的。但是,现在使用jQuery._data( element, "events" )是获取此信息的“正确”方式。 - gnarf

35

我不好意思做广告,但你可以使用findHandlerJS

你只需要包含findHandlersJS(或者只需将原始javascript代码复制并粘贴到Chrome浏览器的控制台窗口),然后指定事件类型和jQuery选择器,以查找你感兴趣的元素的事件处理程序。

例如,你可以通过以下方式快速查找你提到的事件处理程序:

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

这是返回的内容:

  • element
    事件处理程序实际注册的元素
  • events
    包含有关我们感兴趣的事件类型(例如click,change等)的jQuery事件处理程序信息的数组
    • handler
      您可以右键单击并选择“显示函数定义”以查看的实际事件处理程序方法
    • selector
      委托事件提供的选择器。 对于直接事件,它将为空。
    • targets
      此事件处理程序针对的元素列表。 例如,对于在文档对象中注册并针对页面中所有按钮的委派事件处理程序,此属性将列出页面中的所有按钮。 您可以将鼠标悬停在它们上面,并在Chrome中看到它们被突出显示。

您可以在此处进行尝试


13
我使用 eventbug 插件来调试此目的。

谢谢,非常有用的提示。该扩展程序会在Firebug中添加一个选项卡(“事件”),显示页面的事件,以便您可以轻松地浏览它们。 - Gruber
12
此外,Chrome开发者工具在“元素”选项卡下有“事件监听器”,在“源代码”选项卡下有“事件监听器断点”。 - clayzermk1

12

我将@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设置的事件。


6

检查元素上的事件:

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

请注意,这仅适用于直接事件处理程序。如果您正在使用$(document).on("event-name", "jq-selector", function() { //logic }) ,您将需要查看本答案底部的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,但公司需要它...)。编辑:$._data 包含元素的重复事件,即使在FF中它不包含任何事件...奇怪的IE世界。但是要注意可能存在重复事件的可能性。 - Dominik Szymański
哦,汤姆,实际上是你的代码在每次执行此方法时都会使事件倍增。不好。 - Dominik Szymański

5
截至1.9版本,除了使用迁移插件恢复旧有行为外,没有其他文档记录的方法来检索事件。你可以像jps提到的那样使用_.data()方法,但那是一种内部方法。所以如果需要这个功能,请做正确的事情并使用Migrate插件。jQuery关于.data("events")的文档如下:
在1.9之前,如果没有其他代码定义了名称为"events"的数据元素,则可以使用.data("events")来检索元素的jQuery未记录的内部事件数据结构。这种特殊情况在1.9中已被删除。没有公共接口来检索这个内部数据结构,它仍然没有文档记录。然而,jQuery Migrate插件为依赖它的代码恢复了这个行为。

被接受的答案还清晰地展示了获取最新版本中其值的新、正确方法:jQuery._data( elem, "events" );... - Ian
2
一个私有的、未记录的方式永远不会是正确的方式。正确的方式——也就是记录、公开和预期的方式——是使用迁移插件。 - oligofren
4
您好,Migrate插件的重点似乎被您误解了。jQuery移除了一些不建议使用的功能,而Migrate插件是为了帮助开发者将他们的代码迁移到新版本,以便他们能够立即利用新功能和改进,但不会损失任何功能。它旨在帮助编码人员了解他们需要做什么才能开始正确地使用新版本的jQuery。您不应该在生产中使用它来恢复功能。此外,许多内容在jQuery文档中未得到记录和更新 - 他们以前指出过,所以这不是一个原因。 - Ian
此外,如果它被包含在jQuery博客的建议中,我会使用它:http://blog.jquery.com/2012/08/09/jquery-1-8-released/ - Ian
你关于 Migrate 插件的推理看起来是合理的。如果我删除我的答案可以吗? - oligofren

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

这将返回具有单击处理程序的元素。

3

在具备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: Cannot read property 'EVENT_NAME' of undefined,位于 <anonymous>:1:62 - Mike W
'ct100_ContentPlaceHolder1_lcsSection' 是一个字符串,而不是 DOM 元素。 - Jesan Fafon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接