jQuery查找向对象注册的事件处理程序


555

我需要查找在对象上注册了哪些事件处理程序。

例如:

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

$("#el")已注册点击鼠标悬停

是否有一个函数可以找出并可能遍历事件处理程序?

如果通过适当的方法无法在jQuery对象上进行操作,是否可以在普通的DOM对象上进行处理?


5
不幸的是,现在:bugs.jquery.com/ticket/10589
Skylar

2
同时支持jQuery之前和之后的1.8版本:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam

2
请注意,您可以使用FF和Chrome开发工具(F12)查看这些事件侦听器。见developers.google.com/web/tools/chrome-devtools/debug/...developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/...
oriadam

Answers:


691

从jQuery 1.8开始,“公共API”不再提供事件数据。阅读此jQuery博客文章。您现在应该改为使用此:

jQuery._data( elem, "events" );

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

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

在旧版本的jQuery中,您可能必须使用旧方法,即:

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

222
但您仍然可以使用 $._data($(elem).get(0), "events")
bullgare 2012年

10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data(“ events”):jQuery将其与事件相关的数据存储在每个对象上名为(等待它)事件的数据对象中。这是一个内部数据结构,因此在1.8中,它将从用户数据名称空间中删除,因此不会与同名项目冲突。仍然可以通过jQuery._data(element,“ events”)访问jQuery的事件数据,但是请注意,这是一个内部数据结构,未记录且不应进行修改。
山姆·格林哈

我一直在使用这种方法来尝试找出按钮的click事件。在Chrome控制台中,它显示handler: function () {在click属性中。我必须双击功能部分才能使其扩展并显示功能的全部内容。
2015年

@jim yeaaah,双击就是答案
Adib Aroui 2015年

2
无缝支持这两种选择:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam

84

您可以通过抓取事件来实现(从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
这是正确的答案。关键是下半场。这帮助我在不到一分钟的时间内找到了一个问题,如果我必须搜索所有代码,那将花费一个多小时。谢谢!
Andrew Ensley 2010年

2
适用于1.4,但不适用于jQuery 1.8.2。
TimoKähkönen'12

15
对于jQuery 1.8+,您必须使用“私有数据”方法:jQuery._data( jQuery("#el")[0], "events" );而不是“公共数据”方法:jQuery("#el").data("events")。该events对象实际上尚未存储.data()很长时间,我们通过从“公共API”中删除此“代理”来削减了一些字节的代码
gnarf 2013年

36

对于jQuery 1.8+,这将不再起作用,因为内部数据位于另一个对象中。

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

下划线(“ _”)是造成区别的原因。在内部,它正在调用$.data(element, name, null, true),最后一个(第四个)参数是一个内部参数(“ pvt”)。


$ ._ data(“ body”,“ events”)未定义$()。jquery; “ 1.7.1”(始终“未定义”尝试1.7.2和1.8.1)
Mars Robertson

2
@Michal-api.jquery.com/ jQuery.data说它接受一个元素,而不是选择器。
PhistucK 2012年

1
现在可以正常工作:$ ._ data($(“ body”)。get(0),“ events”)甚至更好:$(“ body”)。data(“ events”)!
火星罗伯逊

2
FWIW-指出不需要“内部”使用我们明确未记录的参数调用其他数据函数。但是是的,这jQuery._data( element, "events" )是现在获取此信息的“正确”方法。
gnarf

34

无耻的插件,但是您可以使用findHandlerJS

要使用它,您只需要包括findHandlersJS(或仅将原始javascript代码复制并粘贴到chrome的控制台窗口中),并为您感兴趣的元素指定事件类型和jquery选择器。

对于您的示例,您可以通过执行以下操作快速找到您提到的事件处理程序

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

这是返回的内容:

  • 元素
    在其中注册了事件处理程序的实际元素
  • events
    数组,其中包含有关我们感兴趣的事件类型(例如,单击,更改等)的jquery事件处理程序的信息
    • 处理
      实际事件处理方法,你可以通过右击它并选择显示函数定义见
    • 选择
      器为委托事件提供的选择器。对于直接事件,它将为空。
    • 目标
      列出此事件处理程序目标的元素。例如,对于在文档对象中注册并以页面中所有按钮为目标的委托事件处理程序,此属性将列出页面中的所有按钮。您可以将鼠标悬停在chrome上。

你可以在这里尝试


我认为这应该是公认的答案。那也是唯一对我有用的。这是非常彻底的,因为遍历了搜索事件的所有元素。
Marquinho Peli

12

我使用eventbug插件为此目的进行调试


谢谢,很好的提示。该扩展程序在Firebug中添加了一个标签(“事件”),用于显示页面的事件,因此您可以轻松浏览它们。
Gruber

11
此外,Chrome开发者工具在“元素”标签下具有“事件监听器”,在“源”标签下具有“事件监听器断点”。
clayzermk1

10

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


5

从1.9版本开始,除了使用Migrate插件还原旧行为外,没有记录文档的方法来检索事件。您可以使用jps提到的_.data()方法,但这是一个内部方法。因此,如果需要此功能,请执行正确的操作并使用Migrate插件。

从jQuery文档开始 .data("events")

在1.9之前,如果没有其他代码定义名称为“ events”的数据元素,则可以使用.data(“ events”)检索jQuery的未记录内部事件数据结构。此特殊情况已在1.9中删除。没有公共接口可检索此内部数据结构,并且该文件仍未记录。但是,jQuery Migrate插件为依赖它的代码恢复了此行为。


公认的答案也清楚地显示了针对最新版本进行更新的正确方法:jQuery._data( elem, "events" );...
Ian

2
不公开记录的私人方式永远不会是正确的方式。正确的方法(即已记录的,公开的和预期的)是使用Migrate插件。
oligofren

3
您似乎误解了Migrate插件的要点。jQuery删除了过时的功能,而Migrate插件用于帮助开发人员的代码迁移到较新的版本,以便他们可以立即利用新功能和改进功能,而又不会失去功能。旨在帮助编码人员了解他们需要做什么才能正确使用新版本的jQuery。您不应该在生产中使用它来还原功能。另外,jQuery文档中未记录许多最新信息,因为它们之前已经指出,所以这不是原因
Ian

另外,如果它作为建议包含在jQuery博客中,我将使用它:blog.jquery.com/2012/08/09/jquery-1-8-released
Ian

您对Migrate插件的推理似乎很合理。如果我删除答案,可以吗?
oligofren

5

要检查元素上的事件:

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世界。但是,请务必注意发生重复事件的可能性。
DominikSzymański

哦,汤姆,实际上是您的代码在每次执行此方法时将事件相乘。不好。
DominikSzymański

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

这将返回附加了单击处理程序的元素。


2

在带有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:无法读取在<anonymous>:1:62处未定义的属性“ EVENT_NAME”
Mike W

'ct100_ContentPlaceHolder1_lcsSection'是一个字符串,而不是DOM元素。
Jesan Fafon

2

可以使用以下方法检索事件:

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

或jQuery 1.8+:

jQuery._data(elem, 'events');

注意:$('selector').live('event', handler) 可以使用以下方法检索使用 限制的事件:

jQuery(document).data('events')

2
jQuery(document).data('events')给我未定义
Mike W

1

我不得不说很多答案都很有趣,但是最近我遇到了类似的问题,通过采用DOM方式,解决方案非常简单。这是不同的,因为您不进行迭代,而是直接针对您需要的事件,但是在下文中,我将给出更一般的答案。

我连续有一张图片:

<table>
  <td><tr><img class="folder" /></tr><tr>...</tr></td>
</table>

该图像上附加了click事件处理程序:

imageNode.click(function () { ... });

我的意图是将可点击区域扩展到整个行,因此我首先获得了所有图像和相对行:

tableNode.find("img.folder").each(function () {
  var tr;

  tr = $(this).closest("tr");
  // <-- actual answer
});

现在,在实际的anwer行中,我做了如下操作,给出了原始问题的答案:

tr.click(this.onclick);

因此,我直接从DOM元素中获取了事件处理程序,并将其放入jQuery click事件处理程序中。奇迹般有效。

现在,到一般情况。在jQuery以前的旧时代,您可以使用两个简单而强大的功能(由Douglas Crockford赠予我们的凡人)将所有事件附加到对象上:

function walkTheDOM(node, func)
{
  func(node);
  node = node.firstChild;
  while (node)
  {
    walkTheDOM(node, func);
    node = node.nextSibling;
  }
}

function purgeEventHandlers(node)
{
  walkTheDOM(node, function (n) {
    var f;

    for (f in n)
    {
      if (typeof n[f] === "function")
      {
        n[f] = null;
      }
    }
  });
}


0

做到这一点的另一种方法是只使用jQuery来获取元素,然后使用实际的Javascript获取并设置并使用事件处理程序。例如:

var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;

1
我认为jQuery会进行事件处理优化,我认为这是您的代码所绕过的。
Emile Bergeron

谢谢,是的,我感觉这很棘手-是否有很好的链接来了解有关该优化的更多信息?
tempranova '16

0

我结合了上面的一些答案,并创建了这个看上去疯狂但功能强大的脚本,该脚本希望列出给定元素上的大多数事件侦听器。随时在这里对其进行优化。

var element = $("#some-element");

// sample event handlers
element.on("mouseover", function () {
  alert("foo");
});

$(".parent-element").on("mousedown", "span", function () {
  alert("bar");
});

$(document).on("click", "span", function () {
  alert("xyz");
});

var collection = element.parents()
  .add(element)
  .add($(document));
collection.each(function() {
  var currentEl = $(this) ? $(this) : $(document);
  var tagName = $(this)[0].tagName ? $(this)[0].tagName : "DOCUMENT";
  var events = $._data($(this)[0], "events");
  var isItself = $(this)[0] === element[0]
  if (!events) return;
  $.each(events, function(i, event) {
    if (!event) return;
    $.each(event, function(j, h) {
      var found = false;        
      if (h.selector && h.selector.length > 0) {
        currentEl.find(h.selector).each(function () {
          if ($(this)[0] === element[0]) {
            found = true;
          }
        });
      } else if (!h.selector && isItself) {
        found = true;
      }

      if (found) {
        console.log("################ " + tagName);
        console.log("event: " + i);
        console.log("selector: '" + h.selector + "'");
        console.log(h.handler);
      }
    });
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="parent-element">
  <span id="some-element"></span>
</div>


0

jQuery不允许您仅访问给定元素的事件。您可以使用未记录的内部方法访问它们

$._data(element, "events")

但是它仍然不会为您提供所有事件,确切地说,不会向您显示分配有

$([selector|element]).on()

这些事件存储在文档中,因此您可以通过浏览来获取它们

$._data(document, "events")

但这是一项艰苦的工作,因为整个网页都有很多活动。

Tom G上面创建了一个函数,该函数仅针对给定元素的事件过滤文档并合并这两种方法的输出,但是它具有在输出中复制事件的缺陷(并有效地使元素的jQuery内部事件列表与应用程序发生了混乱)。我已修复该缺陷,您可以在下面找到代码。只需将其粘贴到开发控制台或应用程序代码中,然后在需要时执行它即可获得给定元素的所有事件的完整列表。

需要注意的是,元素实际上是HTMLElement,而不是jQuery对象。

function getEvents(element) {
    var elemEvents = $._data(element, "events");
    var allDocEvnts = $._data(document, "events");
    function equalEvents(evt1, evt2)
    {
        return evt1.guid === evt2.guid;
    }

    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] = [];
                    }
                    if(!elemEvents[evntType].some(function(evt) { return equalEvents(evt, evts[i]); })) {
                        elemEvents[evntType].push(evts[i]);
                    }
                }
            }
        }
    }
    return elemEvents;
}
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.