jQuery tìm các trình xử lý sự kiện đã đăng ký với một đối tượng


555

Tôi cần tìm trình xử lý sự kiện nào được đăng ký trên một đối tượng.

Ví dụ:

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

$("#el")đã nhấp và đăng ký di chuột .

Có một chức năng để tìm ra điều đó, và có thể lặp đi lặp lại qua các trình xử lý sự kiện?

Nếu không thể trên một đối tượng jQuery thông qua các phương thức thích hợp, thì có thể trên một đối tượng DOM đơn giản không?


5
thật không may, ngay bây giờ: bug.jquery.com/ticket/10589
Skylar Saveland

2
hỗ trợ cả jQuery trước và bài 1.8:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam

2
Lưu ý rằng bạn có thể sử dụng các công cụ phát triển FF và Chrome (F12) để xem các trình lắng nghe sự kiện này. Xem nhà phát
triển.google.com/web/tools/chrome

Câu trả lời:


691

Kể từ jQuery 1.8, dữ liệu sự kiện không còn có sẵn từ "API công khai" cho dữ liệu. Đọc bài viết trên blog jQuery này . Bây giờ bạn nên sử dụng cái này thay thế:

jQuery._data( elem, "events" );

elem phải là Phần tử HTML, không phải là đối tượng jQuery hoặc bộ chọn.

Xin lưu ý rằng đây là cấu trúc nội bộ, 'riêng tư' và không nên sửa đổi. Chỉ sử dụng cho mục đích gỡ lỗi.

Trong các phiên bản cũ hơn của jQuery, bạn có thể phải sử dụng phương thức cũ đó là:

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

222
nhưng bạn vẫn có thể sử dụng $._data($(elem).get(0), "events")
bullgare

10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data (các sự kiện của vụ kiện): jQuery lưu trữ dữ liệu liên quan đến sự kiện của nó trong một đối tượng dữ liệu có tên (chờ cho nó) các sự kiện trên mỗi thành phần. Đây là cấu trúc dữ liệu nội bộ, vì vậy, trong 1.8, phần này sẽ bị xóa khỏi không gian tên dữ liệu người dùng để nó không xung đột với các mục cùng tên. Dữ liệu sự kiện của jQuery vẫn có thể được truy cập thông qua jQuery._data (phần tử, "sự kiện") nhưng lưu ý rằng đây là cấu trúc dữ liệu nội bộ không có giấy tờ và không nên sửa đổi.
Sam Greenhalgh

Tôi đã sử dụng phương pháp này để thử và tìm hiểu sự kiện nhấp chuột của một nút. Trong bảng điều khiển Chrome, nó hiển thị handler: function () {bên trong thuộc tính nhấp chuột. Tôi đã phải nhấp đúp chuột vào phần chức năng để nó mở rộng và hiển thị đầy đủ nội dung của chức năng.
Jim

@jim yeaaah, nhấp đúp chuột là câu trả lời
Adib Aroui

2
Hỗ trợ liền mạch cả hai tùy chọn:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam

84

Bạn có thể làm điều đó bằng cách thu thập dữ liệu các sự kiện (kể từ jQuery 1.8+), như thế này:

$.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
  });
});

Đây là một ví dụ bạn có thể chơi với:

$(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
Đây là câu trả lời chính xác. Các bit quan trọng là nửa thứ hai. Điều này giúp tôi tìm ra một vấn đề trong vòng một phút sẽ mất hơn một giờ nếu tôi phải tìm kiếm trong tất cả các mã. Cảm ơn bạn!
Andrew Oblley

2
Hoạt động với 1.4, nhưng không có trong jQuery 1.8.2.
Timo Kähkönen

15
Đối với jQuery 1.8+, bạn phải sử dụng phương thức 'dữ liệu riêng tư': jQuery._data( jQuery("#el")[0], "events" );thay vì phương thức 'dữ liệu công cộng' : jQuery("#el").data("events"). Đối eventstượng đã không thực sự được lưu trữ trong .data()một thời gian dài, chúng tôi đã cắt bớt một vài byte mã bằng cách xóa "proxy" này khỏi "API công khai"
gnarf

36

Đối với jQuery 1.8+, điều này sẽ không còn hoạt động vì dữ liệu nội bộ được đặt trong một đối tượng khác.

Cách thức không chính thức mới nhất (nhưng cũng hoạt động trong các phiên bản trước, ít nhất là trong 1.7.2) cách thực hiện bây giờ là - $._data(element, "events")

Dấu gạch dưới ("_") là điều tạo nên sự khác biệt ở đây. Trong nội bộ, nó đang gọi $.data(element, name, null, true), tham số cuối cùng (thứ tư) là một tham số nội bộ ("pvt").


$ ._ dữ liệu ("cơ thể", "sự kiện") không xác định $ (). jquery; "1.7.1" (đã thử 1.7.2 và 1.8.1 mọi lúc "không xác định")
Mars Robertson

2
@Michal - api.jquery.com/jQuery.data nói rằng nó chấp nhận một phần tử, không phải là bộ chọn.
PhistucK

1
Bây giờ hoạt động tốt: $ ._ data ($ ("body"). Get (0), "event") Hoặc thậm chí tốt hơn: $ ("body"). Data ("event")!
Mars Robertson

2
FWIW - Chỉ ra rằng nó 'bên trong' gọi hàm dữ liệu khác với một tham số mà chúng tôi đặc biệt không có tài liệu có thể không cần thiết. Nhưng có, jQuery._data( element, "events" )là cách 'chính xác' để có được thông tin này ngay bây giờ.
gnarf

34

Ổ cắm không biết xấu hổ, nhưng bạn có thể sử dụng findHandlerJS

Để sử dụng nó, bạn chỉ cần bao gồm findHandlersJS (hoặc chỉ sao chép và dán mã javascript thô vào cửa sổ bảng điều khiển của chrome) và chỉ định loại sự kiện và bộ chọn jquery cho các yếu tố bạn quan tâm.

Ví dụ của bạn, bạn có thể nhanh chóng tìm thấy các trình xử lý sự kiện mà bạn đã đề cập bằng cách thực hiện

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

Đây là những gì được trả lại:

  • phần tử
    Phần tử thực tế nơi trình xử lý sự kiện đã được đăng ký
  • sự kiện
    Mảng với thông tin về các trình xử lý sự kiện jquery cho loại sự kiện mà chúng ta quan tâm (ví dụ: nhấp, thay đổi, v.v.)
    • handler
      Phương thức xử lý sự kiện thực tế mà bạn có thể thấy bằng cách nhấp chuột phải vào nó và chọn Hiển thị định nghĩa hàm
    • bộ chọn
      Bộ chọn được cung cấp cho các sự kiện được ủy quyền. Nó sẽ trống cho các sự kiện trực tiếp.
    • mục tiêu
      Danh sách với các yếu tố mà trình xử lý sự kiện này nhắm mục tiêu. Ví dụ: đối với một trình xử lý sự kiện được ủy nhiệm được đăng ký trong đối tượng tài liệu và nhắm mục tiêu tất cả các nút trong một trang, thuộc tính này sẽ liệt kê tất cả các nút trong trang. Bạn có thể di chuột và xem chúng được tô sáng bằng chrome.

Bạn có thể thử nó ở đây


Tôi nghĩ rằng đó nên là câu trả lời được chấp nhận. Đó là người duy nhất làm việc cho tôi quá. Nó rất kỹ lưỡng kể từ khi đi qua tất cả các yếu tố tìm kiếm sự kiện.
Marquinho Peli

12

Tôi sử dụng plugin eventbird để fireorms cho mục đích này.


Cảm ơn, mẹo tuyệt vời. Tiện ích mở rộng thêm một tab vào Fireorms ("Sự kiện") hiển thị các sự kiện của trang, do đó bạn có thể hiển thị chúng dễ dàng.
Gruber

11
Ngoài ra, Công cụ dành cho nhà phát triển Chrome có "Trình lắng nghe sự kiện" trong tab "Thành phần" và "Điểm dừng của trình lắng nghe sự kiện" trong tab "Nguồn".
clayzermk1

10

Tôi đã kết hợp cả hai giải pháp từ @jps đến một chức năng:

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 {};
};

Nhưng hãy cẩn thận, hàm này chỉ có thể trả về các sự kiện đã được đặt bằng chính jQuery.


5

Kể từ 1.9, không có cách nào ghi lại các sự kiện, ngoài việc sử dụng plugin Migrate để khôi phục hành vi cũ. Bạn có thể sử dụng phương thức _.data () như jps đề cập, nhưng đó là một phương thức nội bộ. Vì vậy, chỉ cần làm đúng và sử dụng plugin Di chuyển nếu bạn cần chức năng này.

Từ tài liệu jQuery trên .data("events")

Trước 1.9, .data ("sự kiện") có thể được sử dụng để truy xuất cấu trúc dữ liệu sự kiện nội bộ không có tài liệu của jQuery cho một phần tử nếu không có mã nào khác xác định thành phần dữ liệu có tên "sự kiện". Trường hợp đặc biệt này đã được gỡ bỏ trong 1.9. Không có giao diện chung để truy xuất cấu trúc dữ liệu nội bộ này và nó vẫn không có giấy tờ. Tuy nhiên, plugin jQuery Migrate khôi phục hành vi này cho mã phụ thuộc vào nó.


Câu trả lời được chấp nhận cũng cho thấy rõ cách thức mới, chính xác để có được nó cho các phiên bản gần đây: jQuery._data( elem, "events" );...
Ian

2
Một cách riêng tư, không có giấy tờ sẽ không bao giờ là một cách chính xác . Cách chính xác - có nghĩa là tài liệu, công khai và dự định - là sử dụng plugin Di chuyển.
oligofren

3
Bạn dường như hiểu sai quan điểm của plugin Di chuyển. jQuery đã loại bỏ các tính năng không dùng nữa và plugin Migrate là để giúp di chuyển mã của nhà phát triển sang các phiên bản mới hơn để họ có thể ngay lập tức tận dụng các tính năng và cải tiến mới, nhưng không bị mất chức năng. Điều này có nghĩa là để giúp người viết mã thấy những gì họ cần làm để bắt đầu sử dụng đúng các phiên bản mới của jQuery. Bạn không nên sử dụng nó trong sản xuất để khôi phục các tính năng. Ngoài ra, nhiều điều không được ghi lại và cập nhật trong tài liệu jQuery - họ đã chỉ ra điều đó trước đây, vì vậy đó không phải là lý do
Ian

Ngoài ra, nếu nó được đưa vào như một đề xuất trong blog jQuery, tôi sẽ sử dụng nó: blog.jquery.com/2012/08/09/jquery-1-8- phát hành
Ian

Lý luận của bạn về plugin Migrate có vẻ hợp lý. OK nếu tôi xóa câu trả lời của tôi?
oligofren

5

Để kiểm tra các sự kiện trên một phần tử:

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

Lưu ý rằng điều này sẽ chỉ hoạt động với các trình xử lý sự kiện trực tiếp, nếu bạn đang sử dụng $ (document) .on ("tên sự kiện", "jq-selector", function () {// logic}), bạn sẽ muốn xem Hàm getEvents ở cuối câu trả lời này

Ví dụ:

 var events = $._data(document.getElementById("myElemId"), "events")

hoặc là

 var events = $._data($("#myElemId")[0], "events")

Ví dụ đầy đủ:

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

Một cách đầy đủ hơn để kiểm tra, bao gồm các trình nghe động, được cài đặt với $ (tài liệu) .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;
}

Ví dụ sử dụng:

getEvents($('#myElemId')[0])

Đây getEventslà phương pháp overally tuyệt vời, nhưng điều này là, nó mang lại cho các mục trùng lặp trong IE11 (Tôi biết, IE lần nữa, nhưng nhu cầu của công ty nó ...). EDIT: $ ._ dữ liệu chứa các sự kiện trùng lặp cho phần tử, mặc dù trên FF nó không chứa bất kỳ ... thế giới IE kỳ lạ nào. Nhưng điều quan trọng là phải coi chừng khả năng có các sự kiện trùng lặp này.
Dominik Szymański

Ồ, Tom, thực ra mã của bạn là nhân các sự kiện với mỗi lần thực hiện phương thức này. Không tốt.
Dominik Szymański

4

Tôi đã tạo một bộ chọn jQuery tùy chỉnh để kiểm tra cả bộ đệm của bộ xử lý sự kiện được gán cũng như các phần tử sử dụng phương thức gốc để thêm chúng:

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

Thí dụ:

$(":event(click)")

Điều này sẽ trả về các phần tử có trình xử lý nhấp chuột được gắn vào chúng.


2

Trong trình duyệt hiện đại có ECMAScript 5.1 / Array.prototype.map, bạn cũng có thể sử dụng

jQuery._data(DOCUMENTELEMENT,'events')["EVENT_NAME"].map(function(elem){return elem.handler;});

trong bảng điều khiển trình duyệt của bạn, nơi sẽ in nguồn của trình xử lý, được phân cách bằng dấu phẩy. Hữu ích để liếc nhìn những gì tất cả đang chạy trên một sự kiện cụ thể.


jQuery._data('ct100_ContentPlaceHolder1_lcsSection','events')["EVENT_NAME"].map(function(elem){return elem.handler;}); Uncaught TypeError: Không thể đọc thuộc tính 'EVENT_NAME' không xác định tại <nặc danh>: 1: 62
Mike W

'ct100_ContentPlaceHolder1_lcsSection'là một chuỗi, không phải là Phần tử DOM.
Jesan Fafon 17/03/2017

2

Các sự kiện có thể được lấy ra bằng cách sử dụng:

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

hoặc jQuery 1.8+:

jQuery._data(elem, 'events');

Lưu ý:$('selector').live('event', handler) Có thể truy xuất các sự kiện giới hạn bằng cách sử dụng:

jQuery(document).data('events')

2
jQuery (tài liệu) .data ('event') mang đến cho tôi không xác định
Mike W

1

Tôi phải nói nhiều câu trả lời rất thú vị, nhưng gần đây tôi có một vấn đề tương tự và giải pháp cực kỳ đơn giản bằng cách đi theo cách của DOM. Nó khác bởi vì bạn không lặp đi lặp lại mà nhắm trực tiếp vào sự kiện bạn cần, nhưng dưới đây tôi sẽ đưa ra câu trả lời chung chung hơn.

Tôi đã có một hình ảnh liên tiếp:

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

Và hình ảnh đó có một trình xử lý sự kiện nhấp vào nó:

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

Ý định của tôi là mở rộng khu vực có thể nhấp vào toàn bộ hàng, vì vậy trước tiên tôi có tất cả hình ảnh và hàng tương đối:

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

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

Bây giờ trong dòng anwer thực tế tôi vừa làm như sau, đưa ra câu trả lời cho câu hỏi ban đầu:

tr.click(this.onclick);

Vì vậy, tôi đã tìm nạp trình xử lý sự kiện trực tiếp từ phần tử DOM và đưa nó vào trình xử lý sự kiện nhấp chuột jQuery. Hoạt động như một lá bùa.

Bây giờ, đến trường hợp chung. Trong những ngày tiền jQuery cũ, bạn có thể nhận được tất cả các sự kiện gắn liền với một đối tượng với hai hàm đơn giản nhưng mạnh mẽ được tặng cho người phàm của chúng ta bởi 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

Một cách khác để làm điều đó là chỉ cần sử dụng jQuery để lấy phần tử, sau đó đi qua Javascript thực tế để nhận và thiết lập và chơi với các trình xử lý sự kiện. Ví dụ:

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

1
Tôi nghĩ rằng jQuery thực hiện tối ưu hóa xử lý sự kiện mà tôi nghĩ bị phá vỡ bởi mã của bạn ở đây.
Emile Bergeron

Cảm ơn, vâng tôi có cảm giác đây là hacky - có liên kết tốt nào để tìm hiểu thêm về tối ưu hóa đó không?
tempranova

0

Tôi đã kết hợp một số câu trả lời ở trên và tạo ra kịch bản có vẻ điên rồ nhưng đầy chức năng này liệt kê hy vọng hầu hết những người nghe sự kiện về yếu tố đã cho. Hãy tối ưu hóa nó ở đây.

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 không cho phép bạn chỉ đơn giản là truy cập các sự kiện cho một phần tử đã cho. Bạn có thể truy cập chúng bằng phương thức nội bộ không có giấy tờ

$._data(element, "events")

Nhưng nó vẫn không cung cấp cho bạn tất cả các sự kiện, chính xác là sẽ không hiển thị cho bạn các sự kiện được chỉ định

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

Những sự kiện này được lưu trữ bên trong tài liệu, vì vậy bạn có thể tìm nạp chúng bằng cách duyệt qua

$._data(document, "events")

nhưng đó là công việc khó khăn, vì có những sự kiện cho toàn bộ trang web.

Tom G ở trên đã tạo chức năng chỉ lọc tài liệu cho các sự kiện của phần tử đã cho và hợp nhất đầu ra của cả hai phương thức, nhưng nó có một lỗ hổng trùng lặp các sự kiện trong đầu ra (và có hiệu quả trong danh sách sự kiện bên trong jQuery của phần tử gây rối với ứng dụng của bạn). Tôi đã sửa lỗi đó và bạn có thể tìm thấy mã bên dưới. Chỉ cần dán nó vào bảng điều khiển dev hoặc vào mã ứng dụng của bạn và thực thi nó khi cần để có danh sách đẹp về tất cả các sự kiện cho phần tử đã cho.

Điều quan trọng cần lưu ý, phần tử thực sự là HTMLE bổ sung, không phải đối tượng 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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.