Cara memeriksa apakah acara klik sudah terikat - JQuery

130

Saya mengikat acara klik dengan tombol:

$('#myButton').bind('click',  onButtonClicked);

Dalam satu skenario, ini dipanggil beberapa kali, jadi ketika saya melakukan itu triggersaya melihat beberapa panggilan ajax yang ingin saya cegah.

Bagaimana saya bindhanya jika tidak terikat sebelumnya.

Berdebu
sumber
1
Man, saya pikir + Konrad Garus memiliki anwser paling aman ( stackoverflow.com/a/6930078/842768 ), pertimbangkan untuk mengubah jawaban yang Anda terima. Bersulang! UPDATE: Periksa + komentar Herbert Peters juga! Itu pendekatan terbaik.
giovannipds
Untuk standar saat ini, lihat jawaban @A Morel di bawah ini ( stackoverflow.com/a/50097988/1163705 ). Lebih sedikit pengkodean dan menghilangkan semua dugaan, algoritme, dan / atau mencari elemen yang ada di luar persamaan.
Xandor

Jawaban:

117

Pembaruan 24 Agustus '12 : Di jQuery 1.8, tidak mungkin lagi untuk mengakses acara elemen menggunakan.data('events') . (Lihat bug ini untuk detailnya.) Dimungkinkan untuk mengakses data yang sama dengan jQuery._data(elem, 'events'), struktur data internal, yang tidak berdokumen dan karenanya tidak 100% dijamin akan tetap stabil. Namun ini seharusnya tidak menjadi masalah, dan baris yang relevan dari kode plugin di atas dapat diubah sebagai berikut:

var data = jQuery._data(this[0], 'events')[type];

Peristiwa jQuery disimpan dalam objek data yang disebut events, sehingga Anda dapat mencari di ini:

var button = $('#myButton');
if (-1 !== $.inArray(onButtonClicked, button.data('events').click)) {
    button.click(onButtonClicked);
}

Akan lebih baik, tentu saja, jika Anda dapat menyusun aplikasi sehingga kode ini hanya dipanggil sekali.


Ini bisa dienkapsulasi menjadi plugin:

$.fn.isBound = function(type, fn) {
    var data = this.data('events')[type];

    if (data === undefined || data.length === 0) {
        return false;
    }

    return (-1 !== $.inArray(fn, data));
};

Anda kemudian dapat menelepon:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}
lonesomeday
sumber
10
+1 untuk hasil edit. Membaca posting ini hari ini dan saya menggunakan 1,8. Terima kasih.
Gigi2m02
2
Terima kasih sudah mengedit. Apakah ini hanya untuk tombol atau bisakah saya juga menggunakannya untuk <a>? Karena ketika saya mencobanya dikatakan jQuery._data()kesalahan berikutTypeError: a is undefined
Houman
Saya akan membungkus garis var data = jQuery._data(this[0], 'events')[type];dalam menangkap mencoba dan mengembalikan palsu dalam tangkapan. Jika tidak ada peristiwa yang terikat pada ini [0] selain panggilan ke [jenis] akan menyebabkan beberapa variasi "Tidak dapat memperoleh properti 'klik' dari referensi yang tidak terdefinisi atau nol" dan jelas itu juga memberi tahu Anda apa yang perlu Anda ketahui.
Robb Vandaveer
Saya tidak yakin apakah objek _data berubah dalam jQuery 2.0.3 tapi saya tidak bisa menggunakan $ .inArray untuk ini. Fungsi yang ingin Anda bandingkan adalah dalam properti dari setiap item data yang disebut "penangan". Saya memodifikasinya untuk menggunakan pernyataan sederhana dan memeriksa kesetaraan string, apa yang saya asumsikan inArray lakukan. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer
mengapa tidak memindahkan pembaruan / edit Anda ke atas? ... Ini adalah jawaban yang tepat sebenarnya.
Don Cheadle
179

Satu lagi cara - tandai tombol tersebut dengan kelas CSS dan filter:

$('#myButton:not(.bound)').addClass('bound').bind('click',  onButtonClicked);

Dalam versi jQuery terbaru ganti binddengan on:

$('#myButton:not(.bound)').addClass('bound').on('click',  onButtonClicked);
Konrad Garus
sumber
4
Luar biasa! Terima kasih Konrad, ini menyentuh sweet spot. Saya suka pendekatan yang elegan dan sederhana seperti ini. Saya cukup yakin ini lebih berkinerja juga, karena setiap elemen tunggal (yang sudah memiliki event handler klik terlampir) tidak harus melakukan pencarian penuh dalam beberapa acara besar yang membandingkan masing-masing. Dalam implementasi saya, saya menamai kelas pembantu yang ditambahkan "klik-terikat", yang menurut saya merupakan opsi yang lebih bisa dikelola: $ (". Daftar-item: tidak (.klik-terikat)"). AddClass ("terikat-klik") ;
Nicholas Petersen
1
Seperti yang dinyatakan dalam jawaban yang diterima: "Akan lebih baik, tentu saja, jika Anda dapat menyusun aplikasi Anda sehingga kode ini hanya dipanggil sekali." Ini menyelesaikan itu.
Jeff Dege
+1 dalam situasi saya, mati / hidup dan ikat / batalkan memang mencegah beberapa panggilan. Ini adalah solusi yang berhasil.
Jay
2
Ini adalah solusi yang lebih baik untuk kinerja karena kode dapat memeriksa keberadaan kelas CSS dan slip mengikat yang sudah ada. Solusi lain menjalankan proses unbind-then-rebind dengan (setidaknya dari apa yang bisa saya katakan) lebih banyak overhead.
Phil Nicholas
1
2 masalah. (saat menggunakannya dalam plug-in yang dapat diikat ke beberapa elemen) Tidak akan berfungsi saat mengikat acara pengubahan ukuran ke objek jendela. Gunakan data ('terikat', 1) alih-alih addClass ('terikat') adalah pendekatan lain yang berfungsi. Ketika menghancurkan acara itu mungkin melakukannya sementara contoh lain bergantung padanya. Jadi, memeriksa apakah instance lain masih digunakan disarankan.
Herbert Peters
59

Jika menggunakan jQuery 1.7+:

Anda dapat menelepon offsebelumnya on:

$('#myButton').off('click', onButtonClicked) // remove handler
              .on('click', onButtonClicked); // add handler

Jika tidak:

Anda bisa melepas ikatannya terlebih dahulu:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler
Naftali alias Neal
sumber
1
Ini bukan solusi yang indah, tapi itu akan ditingkatkan dengan hanya tidak mengikat satu handler: .unbind('click', onButtonClicked). Lihat manual
lonesomeday
16
Anda benar-benar dapat namespace penangan Anda saat Anda menambahkannya, lalu melepas ikatan menjadi sangat sederhana. $(sel).unbind("click.myNamespace");
Marc
@lonesomeday apakah contoh Anda menggunakan 'unbind' dimaksudkan untuk menunjukkan sesuatu yang berbeda? Bukankah .unbind secara efektif sama dengan .off sehingga Anda harus menulis$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim
1
@ Jim Anda akan melihat pertanyaannya diedit tiga tahun setelah komentar saya! (Dan juga di menit-menit segera setelah komentar saya. Konten yang saya komentari sudah tidak ada lagi, itulah sebabnya mungkin sepertinya tidak masuk akal.)
lonesomeday
@lonesomeday hmmm Saya tidak yakin mengapa itu diedit, apakah Anda pikir saya harus mengembalikan?
Naftali alias Neal
8

Cara terbaik yang saya lihat adalah menggunakan live () atau delegate () untuk menangkap acara di orangtua dan tidak di setiap elemen anak.

Jika tombol Anda berada di dalam elemen #parent, Anda dapat mengganti:

$('#myButton').bind('click', onButtonClicked);

oleh

$('#parent').delegate('#myButton', 'click', onButtonClicked);

bahkan jika #myButton belum ada saat kode ini dieksekusi.

Pablonete
sumber
2
Metode yang tidak digunakan lagi
dobs
8

Saya menulis sebuah plugin yang sangat kecil yang disebut "sekali" yang melakukan itu. Jalankan elemen aktif dan nonaktif.

$.fn.once = function(a, b) {
    return this.each(function() {
        $(this).off(a).on(a,b);
    });
};

Dan sederhana:

$(element).once('click', function(){
});
Rodolfo Jorge Nemer Nogueira
sumber
9
.one () akan menjatuhkan handler setelah eksekusi pertama.
Rodolfo Jorge Nemer Nogueira
3
di sini "sekali" adalah untuk melampirkan pawang sekali. "one" di jquery adalah untuk menjalankan sekali, tidak melampirkan sekali.
Shishir Arora
1
Saya tahu saya datang setelah fakta, tetapi tidak offmenghapus semua penangan untuk jenis yang diberikan? Berarti jika ada penangan klik terpisah yang tidak terkait tetapi pada item yang sama, bukankah itu akan dihapus juga?
Xandor
cukup gunakan namespace pada acara tersebut sehingga tidak menjatuhkan semua penangan seperti: 'keypup.test123'
SemanticZen
6

Kenapa tidak menggunakan ini

unbind() sebelum bind()

$('#myButton').unbind().bind('click',  onButtonClicked);
Krillehbg
sumber
1
$('#myButton').off().on('click', onButtonClicked);juga bekerja untukku.
Veerakran Sereerungruangkul
Bekerja untuk saya ... solusi gr8
imdadhusen
Cantik. Berfungsi sempurna untuk saya untuk tombol yang sudah terikat.
seanbreeden
3

Ini versi saya:

Utils.eventBoundToFunction = function (element, eventType, fCallback) {
    if (!element || !element.data('events') || !element.data('events')[eventType] || !fCallback) {
        return false;
    }

    for (runner in element.data('events')[eventType]) {
        if (element.data('events')[eventType][runner].handler == fCallback) {
            return true;
        }

    }

    return false;
};

Pemakaian:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)
Yair Galler
sumber
2

Berdasarkan jawaban @ konrad-garus, tetapi menggunakan data, karena saya percaya classsebagian besar harus digunakan untuk styling.

if (!el.data("bound")) {
  el.data("bound", true);
  el.on("event", function(e) { ... });
}
Ariel
sumber
2

Untuk menghindari memeriksa / mengikat / melepaskan ikatan, Anda dapat mengubah pendekatan Anda! Mengapa Anda tidak menggunakan Jquery .on ()?

Karena Jquery 1.7, .live (), .delegate () sudah tidak digunakan lagi, sekarang Anda dapat menggunakan .on () untuk

Lampirkan pengendali acara untuk semua elemen yang cocok dengan pemilih saat ini, sekarang dan di masa depan

Ini berarti bahwa Anda dapat melampirkan acara ke elemen induk yang masih ada dan melampirkan elemen anak-anak apakah mereka ada atau tidak!

Saat Anda menggunakan .on () seperti ini:

$('#Parent').on('click', '#myButton'  onButtonClicked);

Anda menangkap peristiwa klik pada orangtua dan itu mencari anak '#myButton' jika ada ...

Jadi, ketika Anda menghapus atau menambahkan elemen turunan, Anda tidak perlu khawatir tentang apakah akan menambah atau menghapus acara yang mengikat.

A. Morel
sumber
Ini benar-benar jawaban terbaik di sini untuk standar saat ini. Saya tidak berpikir fitur pengaturan pawang pada orang tua untuk menonton untuk anak telah diiklankan dengan baik tetapi merupakan solusi yang agak elegan. Saya mengalami kebakaran acara beberapa kali dan setiap kali jumlah total yang dipecat terus meningkat. Baris tunggal ini melakukan seluruh trik dan tanpa menggunakan .offyang saya yakin akan menghapus penangan yang tidak terkait dalam proses.
Xandor
1

Mencoba:

if (typeof($("#myButton").click) != "function") 
{
   $("#myButton").click(onButtonClicked);
}
Mukhtiar Zamin
sumber
0
if ($("#btn").data('events') != undefined && $("#btn").data('events').click != undefined) {
    //do nothing as the click event is already there
} else {
    $("#btn").click(function (e) {
        alert('test');
    });
}
Bishoy Hanna
sumber
0

Pada Juni 2019, saya telah memperbarui fungsi (dan berfungsi untuk apa yang saya butuhkan)

$.fn.isBound = function (type) {
    var data = $._data($(this)[0], 'events');

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};
Carlos Casalicchio
sumber
-2

JQuery memiliki solusi :

$( "#foo" ).one( "click", function() {
  alert( "This will be displayed only once." );
}); 

setara:

$( "#foo" ).on( "click", function( event ) {
  alert( "This will be displayed only once." );
  $( this ).off( event );
});
Veniamin
sumber
1
Jawaban Anda tidak terkait dengan pertanyaan. Pertanyaan adalah tentang fungsi pengikatan ke acara elemen hanya sekali.
Michał Zalewski