jQuery menemukan penangan acara yang terdaftar pada suatu objek

555

Saya perlu menemukan penangan acara mana yang terdaftar atas suatu objek.

Sebagai contoh:

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

$("#el")telah klik dan mouseover terdaftar.

Apakah ada fungsi untuk mengetahuinya, dan mungkin mengulangi event handler?

Jika tidak mungkin pada objek jQuery melalui metode yang tepat, apakah mungkin pada objek DOM biasa?

umur04
sumber
5
sayangnya, sekarang: bugs.jquery.com/ticket/10589
Skylar Saveland
2
mendukung jQuery pre dan post 1.8:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
2
Perhatikan bahwa Anda dapat menggunakan FF dan perangkat pengembang Chrome (F12) untuk melihat pendengar acara ini. Lihat developers.google.com/web/tools/chrome-devtools/debug/… dan developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/…
oriadam

Jawaban:

691

Pada jQuery 1.8, data acara tidak lagi tersedia dari "API publik" untuk data. Baca posting blog jQuery ini . Anda sekarang harus menggunakan ini sebagai gantinya:

jQuery._data( elem, "events" );

elem harus berupa Elemen HTML, bukan objek jQuery, atau pemilih.

Harap dicatat, bahwa ini adalah struktur internal, 'pribadi', dan tidak boleh dimodifikasi. Gunakan ini hanya untuk keperluan debugging.

Di versi jQuery yang lebih lama, Anda mungkin harus menggunakan metode lama yaitu:

jQuery( elem ).data( "events" );
jps
sumber
222
tetapi Anda masih bisa menggunakan $._data($(elem).get(0), "events")
bullgare
10
blog.jquery.com/2011/11/08/building-a-slimmer-jquery .data ("events"): jQuery menyimpan data terkait acara dalam objek data yang bernama (tunggu) peristiwa di setiap elemen. Ini adalah struktur data internal sehingga pada 1.8 ini akan dihapus dari ruang nama data pengguna sehingga tidak akan bertentangan dengan item dengan nama yang sama. Data acara jQuery masih dapat diakses melalui jQuery._data (elemen, "events") tetapi perlu diketahui bahwa ini adalah struktur data internal yang tidak terdokumentasi dan tidak boleh dimodifikasi.
Sam Greenhalgh
Saya telah menggunakan metode ini untuk mencoba dan mengetahui acara klik tombol. Di konsol Chrome itu ditampilkan handler: function () {di dalam properti klik. Saya harus mengklik dua kali pada bagian fungsi untuk memperluas dan menampilkan isi penuh fungsi.
Jim
@jim yeaaah, klik dua kali adalah jawabannya
Adib Aroui
2
Mulus mendukung kedua opsi:var events = (jQuery._data || jQuery.data)(elem, 'events');
oriadam
84

Anda dapat melakukannya dengan merangkak acara (per jQuery 1.8+), seperti ini:

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

Inilah contoh yang bisa Anda mainkan:

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

Nick Craver
sumber
2
Ini jawaban yang benar. Bit penting adalah babak kedua. Ini membantu saya menemukan masalah dalam waktu kurang dari satu menit yang akan memakan waktu lebih dari satu jam jika saya harus mencari semua kode. Terima kasih!
Andrew Ensley
2
Bekerja dengan 1.4, tetapi tidak di jQuery 1.8.2.
Timo Kähkönen
15
Untuk jQuery 1.8+, Anda harus menggunakan metode 'Data pribadi': jQuery._data( jQuery("#el")[0], "events" );bukan metode 'data publik': jQuery("#el").data("events"). The eventsobjek belum benar-benar telah disimpan dalam .data()untuk waktu yang lama, kami dipangkas beberapa byte kode keluar dengan menghapus ini "proxy" dari "API publik"
gnarf
36

Untuk jQuery 1.8+, ini tidak akan berfungsi lagi karena data internal ditempatkan di objek yang berbeda.

Cara tidak resmi terbaru (tetapi berfungsi di versi sebelumnya juga, setidaknya dalam 1.7.2) untuk melakukannya sekarang adalah - $._data(element, "events")

Garis bawah ("_") adalah yang membuat perbedaan di sini. Secara internal, ia memanggil $.data(element, name, null, true), parameter (keempat) terakhir adalah internal ("pvt").

PhistucK
sumber
$ ._ data ("tubuh", "acara") tidak ditentukan $ (). jquery; "1.7.1" (mencoba 1.7.2 dan 1.8.1 sepanjang waktu "tidak terdefinisi")
Mars Robertson
2
@Michal - api.jquery.com/jQuery.data mengatakan ia menerima elemen, bukan pemilih.
PhistucK
1
Sekarang berfungsi dengan baik: $ ._ data ($ ("body"). Dapatkan (0), "events") Atau bahkan lebih baik: $ ("body"). Data ("events")!
Mars Robertson
2
FWIW - Menunjukkan bahwa ia 'secara internal' memanggil fungsi data lain dengan parameter yang secara khusus tidak kami dokumentasikan mungkin tidak diperlukan. Tapi ya, jQuery._data( element, "events" )adalah cara yang 'benar' untuk mendapatkan informasi ini sekarang.
gnarf
34

Steker yang tidak tahu malu, tetapi Anda dapat menggunakan findHandlerJS

Untuk menggunakannya Anda hanya perlu memasukkan findHandlersJS (atau cukup salin & tempel kode javascript mentah ke jendela konsol chrome) dan tentukan jenis acara dan pemilih jquery untuk elemen yang Anda minati.

Sebagai contoh Anda, Anda dapat dengan cepat menemukan penangan acara yang Anda sebutkan dengan melakukan

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

Inilah yang dikembalikan:

  • element Elemen
    aktual tempat pengendali event terdaftar
  • events
    Array dengan informasi tentang penangan acara jquery untuk jenis acara yang menarik bagi kami (mis. klik, ubah, dll.)
    • handler
      Metode event handler aktual yang dapat Anda lihat dengan mengklik kanan dan memilih Tampilkan definisi fungsi
    • selector
      Selector yang disediakan untuk acara yang didelegasikan. Itu akan kosong untuk acara langsung.
    • target
      Daftar dengan elemen-elemen yang ditargetkan oleh event handler ini. Misalnya, untuk pengendali acara yang didelegasikan yang terdaftar di objek dokumen dan menargetkan semua tombol di halaman, properti ini akan mencantumkan semua tombol di halaman. Anda dapat mengarahkannya dan melihatnya disorot dalam chrome.

Anda bisa mencobanya di sini

Rui
sumber
Saya pikir itu harus menjadi jawaban yang diterima. Itu satu-satunya yang bekerja untuk saya juga. Ini sangat menyeluruh karena melewati semua elemen mencari acara.
Marquinho Peli
12

Saya menggunakan plugin EventBug untuk Firebug untuk tujuan ini.

Anton
sumber
Terima kasih, tip yang bagus. Ekstensi menambahkan tab ke Firebug ("Acara") yang menunjukkan acara halaman, sehingga Anda dapat mengeksposnya dengan mudah.
Gruber
11
Selain itu, Alat Pengembang Chrome memiliki "Pendengar Acara" di bawah tab "Elemen" dan "Breakpoint Pendengar Acara" di bawah tab "Sumber".
clayzermk1
10

Saya telah menggabungkan kedua solusi dari @jps ke satu fungsi:

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

Namun waspadalah, fungsi ini hanya dapat mengembalikan acara yang diatur menggunakan jQuery itu sendiri.

algoritme
sumber
5

Pada 1.9, tidak ada cara yang terdokumentasi untuk mengambil kembali acara, selain menggunakan plugin Migrate untuk memulihkan perilaku lama. Anda bisa menggunakan metode _.data () sebagai jps menyebutkan, tetapi itu adalah metode internal. Jadi lakukan saja hal yang benar dan gunakan plugin Migrasi jika Anda memerlukan fungsi ini.

Dari dokumentasi jQuery pada .data("events")

Sebelum 1.9, .data ("events") dapat digunakan untuk mengambil struktur data acara internal jQuery yang tidak didokumentasikan untuk suatu elemen jika tidak ada kode lain yang mendefinisikan elemen data dengan nama "events". Kasing khusus ini telah dihapus pada 1.9. Tidak ada antarmuka publik untuk mengambil struktur data internal ini, dan itu tetap tidak berdokumen. Namun, plugin jQuery Migrate mengembalikan perilaku ini untuk kode yang bergantung padanya.

oligofren
sumber
Jawaban yang diterima juga jelas menunjukkan cara baru yang benar untuk mendapatkannya untuk versi terbaru: jQuery._data( elem, "events" );...
Ian
2
Cara pribadi dan tidak berdokumen tidak akan pernah menjadi cara yang benar . Cara yang benar - artinya didokumentasikan, publik, dan dimaksudkan - adalah dengan menggunakan plugin Migrasi.
oligofren
3
Anda sepertinya salah paham tentang titik migrasi Plugin. jQuery menghapus fitur yang sudah tidak digunakan lagi, dan plugin Migrate adalah untuk membantu memigrasi kode pengembang ke versi yang lebih baru sehingga mereka dapat langsung memanfaatkan fitur dan peningkatan baru, tetapi tidak kehilangan fungsionalitas. Ini dimaksudkan untuk membantu pembuat kode melihat apa yang harus mereka lakukan untuk mulai menggunakan versi jQuery dengan benar. Anda tidak boleh menggunakannya dalam produksi untuk memulihkan fitur. Juga, banyak hal yang tidak didokumentasikan dan mutakhir dalam dokumentasi jQuery - mereka telah menunjukkannya sebelumnya, jadi itu bukan alasan
Ian
Juga, jika itu dimasukkan sebagai saran di blog jQuery, saya akan menggunakannya: blog.jquery.com/2012/08/09/jquery-1-8-released
Ian
Alasan Anda tentang plugin Migrasi tampaknya masuk akal. OK jika saya menghapus jawaban saya?
oligofren
5

Untuk memeriksa acara pada suatu elemen:

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

Perhatikan bahwa ini hanya akan bekerja dengan penangan kejadian langsung, jika Anda menggunakan $ (dokumen) .on ("nama-acara", "pemilih-jq", fungsi () {// logika}), Anda akan ingin melihat fungsi getEvents di bagian bawah jawaban ini

Sebagai contoh:

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

atau

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

Contoh Lengkap:

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

Cara yang lebih lengkap untuk memeriksa, termasuk pendengar dinamis, diinstal dengan $ (dokumen) .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;
}

Contoh penggunaan:

getEvents($('#myElemId')[0])
Tom G
sumber
Ini getEventsmetode adalah overally besar, tetapi hal tersebut, memberikan duplikat entri dalam IE11 (saya tahu, IE lagi, tapi kebutuhan perusahaan itu ...). EDIT: $ ._ data berisi duplikat peristiwa untuk elemen, meskipun pada FF itu tidak mengandung ... Dunia IE aneh. Tetapi penting untuk berhati-hati terhadap kemungkinan memiliki acara duplikat ini.
Dominik Szymański
Oh, Tom, sebenarnya kode Anda yang mengalikan acara dengan setiap eksekusi metode ini. Tidak baik.
Dominik Szymański
4

Saya membuat pemilih jQuery khusus yang memeriksa cache kedua penangan kejadian yang ditugaskan jQuery serta elemen yang menggunakan metode asli untuk menambahkannya:

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

Contoh:

$(":event(click)")

Ini akan mengembalikan elemen yang memiliki penangan klik terpasang padanya.

Erutan409
sumber
2

Di browser modern dengan ECMAScript 5.1 / Array.prototype.map, Anda juga dapat menggunakan

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

di konsol browser Anda, yang akan mencetak sumber dari penangan, dibatasi koma. Berguna untuk melirik apa yang sedang berjalan pada acara tertentu.

Jesan Fafon
sumber
jQuery._data('ct100_ContentPlaceHolder1_lcsSection','events')["EVENT_NAME"].map(function(elem){return elem.handler;}); UnEught TypeError: Tidak dapat membaca properti 'EVENT_NAME' dari undefined di <anonymous>: 1: 62
Mike W
'ct100_ContentPlaceHolder1_lcsSection'adalah string, bukan Elemen DOM.
Jesan Fafon
2

Acara dapat diambil menggunakan:

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

atau jQuery 1.8+:

jQuery._data(elem, 'events');

Catatan: Acara yang dibatasi menggunakan $('selector').live('event', handler) dapat diambil menggunakan:

jQuery(document).data('events')
R. Oosterholt
sumber
2
jQuery (document) .data ('events') memberi saya undefined
Mike W
1

Saya harus mengatakan banyak jawaban yang menarik, tetapi baru-baru ini saya memiliki masalah yang sama dan solusinya sangat sederhana dengan menggunakan cara DOM. Ini berbeda karena Anda tidak mengulangi tetapi mengarahkan langsung pada acara yang Anda butuhkan, tetapi di bawah ini saya akan memberikan jawaban yang lebih umum.

Saya memiliki gambar berturut-turut:

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

Dan gambar itu memiliki pengendali event klik yang melekat padanya:

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

Tujuan saya adalah untuk memperluas area yang dapat diklik ke seluruh baris, jadi saya pertama kali mendapatkan semua gambar dan baris relatif:

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

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

Sekarang di baris anwer yang sebenarnya saya lakukan sebagai berikut, memberikan jawaban untuk pertanyaan awal:

tr.click(this.onclick);

Jadi saya mengambil event handler langsung dari elemen DOM dan memasukkannya ke event handler klik jQuery. Bekerja seperti pesona.

Sekarang, untuk kasus umum. Di masa pra-jQuery dulu, Anda bisa membuat semua acara terlampir pada objek dengan dua fungsi sederhana namun kuat yang diberikan kepada kita manusia oleh 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;
      }
    }
  });
}
pid
sumber
0

Cara lain untuk melakukannya adalah dengan hanya menggunakan jQuery untuk mengambil elemen, kemudian pergi melalui Javascript aktual untuk mendapatkan dan mengatur dan bermain dengan penangan acara. Contohnya:

var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;
tempranova
sumber
1
Saya pikir jQuery melakukan optimasi penanganan event yang saya pikir dielakkan dengan kode Anda di sini.
Emile Bergeron
Terima kasih, ya saya merasa ini hacky - ada tautan bagus untuk mempelajari lebih lanjut tentang pengoptimalan itu?
tempranova
0

Saya menggabungkan beberapa jawaban di atas dan membuat skrip yang terlihat gila namun fungsional ini, yang semoga daftar sebagian besar pendengar acara pada elemen yang diberikan. Jangan ragu untuk mengoptimalkannya di sini.

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>

Marek Lisý
sumber
0

jQuery tidak membiarkan Anda hanya mengakses acara untuk elemen tertentu. Anda dapat mengaksesnya menggunakan metode internal tidak berdokumen

$._data(element, "events")

Tapi itu masih tidak akan memberi Anda semua acara, tepatnya tidak akan menampilkan acara yang ditugaskan kepada Anda

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

Peristiwa ini disimpan di dalam dokumen, sehingga Anda dapat mengambilnya dengan menelusuri

$._data(document, "events")

tapi itu kerja keras, karena ada acara untuk seluruh halaman web.

Tom G di atas menciptakan fungsi yang memfilter dokumen hanya untuk peristiwa elemen yang diberikan dan menggabungkan output dari kedua metode, tetapi memiliki kesalahan duplikasi peristiwa dalam output (dan efektif pada daftar acara internal jQuery elemen yang mengacaukan aplikasi Anda). Saya memperbaiki kekurangan itu dan Anda dapat menemukan kode di bawah ini. Cukup rekatkan ke konsol dev Anda atau ke kode aplikasi Anda dan jalankan ketika diperlukan untuk mendapatkan daftar yang bagus dari semua acara untuk elemen yang diberikan.

Yang penting diperhatikan, elemen sebenarnya HTMLElement, bukan objek 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;
}
Dominik Szymański
sumber