Tıklama olayının zaten bağlı olup olmadığı nasıl kontrol edilir - JQuery


130

Bir düğmeyle bir tıklama olayını bağlıyorum:

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

Bir senaryoda, bu birden çok kez çağrılıyor, bu yüzden bir triggeryaptığımda, önlemek istediğim birden fazla ajax çağrısı görüyorum.

Nasıl bindsadece önceden bağlı değilse.


1
Dostum, bence + Konrad Garus en güvenli cevaplayıcıya sahip ( stackoverflow.com/a/6930078/842768 ), kabul ettiğiniz cevabı değiştirmeyi düşünün. Şerefe! GÜNCELLEME: + Herbert Peters'ın yorumunu da kontrol edin! En iyi yaklaşım budur.
giovannipds

Mevcut standartlar için aşağıdaki @A Morel'in cevabına bakın ( stackoverflow.com/a/50097988/1163705 ). Daha az kodlama ve tüm tahmin çalışmalarını, algoritmaları ve / veya mevcut öğeleri aramayı denklemden çıkarır.
Xandor

Yanıtlar:


118

24 Ağu '12 Güncellemesi : jQuery 1.8'de, öğesini kullanarak öğenin etkinliklerine erişmek artık mümkün değildir .data('events'). (Ayrıntılar için bu hataya bakın.) Aynı verilere jQuery._data(elem, 'events'), belgelenmemiş ve bu nedenle% 100 kararlı kalması garanti edilmeyen dahili bir veri yapısı ile erişmek mümkündür . Bununla birlikte, bu bir sorun olmamalıdır ve yukarıdaki eklenti kodunun ilgili satırı aşağıdaki şekilde değiştirilebilir:

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

jQuery olayları, burada eventsarama yapabilmeniz için adı verilen bir veri nesnesinde saklanır :

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

En iyisi, uygulamanızı bu kod yalnızca bir kez çağrılacak şekilde yapılandırabilmenizdir.


Bu bir eklenti içine alınabilir:

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

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

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

Daha sonra arayabilirsin:

var button = $('#myButton');
if (!button.isBound('click', onButtonClicked)) {
    button.click(onButtonClicked);
}

10
Düzenleme için +1. Bugün bu yazıyı okudum ve 1.8 kullanıyorum. Teşekkürler.
Gigi2m02

2
Düzenlediğiniz için teşekkürler. Bu sadece düğmeler için <a>mi yoksa bunun için de kullanabilir miyim ? Çünkü denediğimde jQuery._data()şu hatayı yazıyorTypeError: a is undefined
Houman

Çizgiyi var data = jQuery._data(this[0], 'events')[type];bir deneme yakalamaya sarar ve yakalamada yanlış döndürürdüm. Bu [0] 'a hiçbir olay bağlı değilse, [tip] çağrısı "Tanımlanmamış veya boş referansın özelliği' tıklama 'özelliği alınamıyor" türünün bir çeşit varyasyonuna neden olur ve açıkçası bu size bilmeniz gerekenleri de söyler.
Robb Vandaveer

_Data nesnesinin jQuery 2.0.3'te değişip değişmediğinden emin değilim ama bunun için $ .inArray kullanamadım. Karşılaştırmak istediğiniz işlev, "işleyici" adı verilen her veri öğesinin özelliğindedir. Basit bir for deyimi kullanmak için değiştirdim ve dize eşdeğerliğini kontrol ettim, inArray'ın yaptığını varsayıyorum. for (var i = 0; i < data.length; i++) { if (data[i].handler.toString() === fn.toString()) { return true; } }
Robb Vandaveer

neden güncellemenizi / düzenlemenizi en üste taşımıyorsunuz? ... Asıl doğru cevap bu.
Don Cheadle

180

Bir yol daha - bu tür düğmeleri bir CSS sınıfıyla işaretleyin ve filtreleyin:

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

Son jQuery sürümlerinde bindşununla değiştirilir on:

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

4
Mükemmel! Teşekkürler Konrad, bu harika noktaya geliyor. Bunun gibi zarif ve basit yaklaşımları seviyorum. Her bir öğenin (tıklama olay işleyicileri eklenmiş olan) her birini karşılaştıran bazı büyük etkinlikler kovasında tam bir arama yapması gerekmediğinden, daha performanslı olduğundan da oldukça eminim. Uygulamamda, daha sürdürülebilir bir seçenek olduğunu düşündüğüm, eklenen yardımcı sınıfı "tıklamaya bağlı" olarak adlandırdım: $ (". List-item: not (.click-bound)"). AddClass ("click-bound") ;
Nicholas Petersen

1
Kabul edilen yanıtta belirtildiği gibi: "En iyisi, başvurunuzu bu kod yalnızca bir kez çağrılacak şekilde yapılandırabilmenizdir." Bu bunu başarır.
Jeff Dege

Benim durumumda +1, off / on ve bind / unbind birden fazla aramayı engelledi. İşe yarayan çözüm buydu.
Jay

2
Bu, performans için daha iyi bir çözümdür çünkü kod, CSS sınıfının varlığını ve zaten mevcut olan slip bağlamayı kontrol edebilir. Diğer çözüm, daha fazla ek yük ile (en azından fr4om, anlatabildiğim kadarıyla) bir çözme sonra yeniden bağlama sürecini yürütür.
Phil Nicholas

1
2 konu. (birkaç öğeye bağlanabilen bir eklentide kullanıldığında) pencere nesnesine bir yeniden boyutlandırma olayını bağlarken çalışmaz. AddClass ('bound') yerine data ('bound', 1) kullanmak, çalışan başka bir yaklaşımdır. Olayı yok ederken, diğer örnekler buna bağlıyken bunu yapabilir. Bu nedenle, diğer örneklerin hala kullanımda olup olmadığını kontrol etmeniz önerilir.
Herbert Peters

59

JQuery 1.7+ kullanıyorsanız:

Daha offönce arayabilirsin on:

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

Değilse:

İlk olayı çözebilirsiniz:

$('#myButton').unbind('click', onButtonClicked) //remove handler
              .bind('click', onButtonClicked);  //add handler

1
Bu tam güzel bir çözüm değildir, ancak yalnızca bir işleyicisi unbinding iyileştirilebilir olacaktır: .unbind('click', onButtonClicked). Kılavuza bakın
lonesomeday

16
İşleyicilerinizi ekledikçe aslında ad alanı yapabilirsiniz, ardından bağın kaldırılması oldukça basit hale gelir. $(sel).unbind("click.myNamespace");
Marc

@lonesomeday, farklı bir şey göstermek için "unbind" kullanan örneğiniz miydi? .Unbind, .off ile aynı şey değildir, bu yüzden yazmanız gerekir$('#myButton').unbind('click', onButtonClicked).bind('click', onButtonClicked);
Jim

1
@ Jim Sorunun yorumumdan üç yıl sonra düzenlendiğini göreceksiniz! (Ve ayrıca yorumumdan hemen sonraki dakikalarda. Yorum yaptığım içerik artık mevcut değil, bu yüzden muhtemelen pek mantıklı görünmüyor.)
yalnızlık

@lonesomeday hmmm Neden düzenlendiğinden emin değilim, sence geri almalı mıyım?
Naftali namı diğer Neal

8

Gördüğüm en iyi yol, olayı her bir alt öğede değil, bir ebeveynde yakalamak için live () veya delegate () kullanmaktır.

Düğmeniz bir #parent öğesinin içindeyse, değiştirebilirsiniz:

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

tarafından

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

Bu kod çalıştırıldığında #myButton henüz mevcut olmasa bile.


2
Kullanımdan kaldırılan yöntemler
dobs

8

Bunu yapan "bir kez" adında çok küçük bir eklenti yazdım. Eleman içinde kapatıp açın.

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

Ve basitçe:

$(element).once('click', function(){
});

9
.one (), ilk çalıştırmadan sonra işleyiciyi bırakacaktır.
Rodolfo Jorge Nemer Nogueira

3
burada "bir kez" bir kez işleyici eklemek içindir. JQuery'deki "bir", bir kez eklenmeden bir kez çalıştırmak içindir.
Shishir Arora

1
Olaydan sonra yoluma girdiğimi biliyorum, ancak offverilen tür için tüm işleyicileri kaldırmıyor mu? Yani, ilgisiz, ancak aynı öğede ayrı bir tıklama işleyicisi olsaydı, o da kaldırılmaz mıydı?
Xandor

sadece olayda bir ad alanı kullanın, böylece tüm işleyicileri bırakmaz: 'keypup.test123'
SemanticZen

6

Neden bunu kullanmıyorsun

unbind() önce bind()

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

1
$('#myButton').off().on('click', onButtonClicked);benim için de çalışıyor.
Veerakran Sereerungruangkul

Benim için çalışıyor ... gr8 çözümü
imdadhusen

Güzel. Zaten bağlı olan bir düğme için mükemmel çalışıyor.
seanbreeden

3

İşte benim versiyonum:

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

Kullanımı:

Utils.eventBoundToFunction(okButton, 'click', handleOkButtonFunction)

2

@ Konrad-garus cevabına dayanıyor, ancak kullanıyor data, çünkü classçoğunlukla stil için kullanılması gerektiğine inanıyorum .

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

2

Kontrol etme / bağlama / çözme işlemlerini önlemek için yaklaşımınızı değiştirebilirsiniz! Neden Jquery .on () kullanmıyorsunuz?

Jquery 1.7, .live (), .delegate () kullanımdan kaldırıldığından, artık .on () kullanarak

Şu anki seçiciyle eşleşen tüm öğeler için şimdi ve gelecekte bir olay işleyicisi ekleyin

Bu, hala var olan bir ana öğeye bir olay ekleyebileceğiniz ve mevcut olsun veya olmasın alt öğeler ekleyebileceğiniz anlamına gelir!

.On () 'u şu şekilde kullandığınızda:

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

Üst öğede olay tıklamasını yakalarsınız ve varsa '#myButton' alt öğesini ararsınız ...

Bu nedenle, bir alt öğeyi kaldırdığınızda veya eklediğinizde, olay bağlamayı ekleyip kaldırmayacağınız konusunda endişelenmenize gerek yoktur.


Mevcut standartlar için buradaki en iyi cevap kesinlikle budur. İşleyiciyi çocuğa bakması için ebeveyne yerleştirmenin bu özelliğinin iyi tanıtıldığını düşünmüyorum, ancak oldukça zarif bir çözüm. Birden çok kez yangın çıkıyordum ve her seferinde toplam ateşleme sayısı artmaya devam ediyordu. Bu tek satır tüm hile yaptı ve bunu kullanmadan .offsüreçteki ilgisiz işleyicileri kaldıracağına inanıyorum.
Xandor

1

Deneyin:

if (typeof($("#myButton").click) != "function") 
{
   $("#myButton").click(onButtonClicked);
}

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');
    });
}

0

Haziran 2019 itibarıyla işlevi güncelledim (ve ihtiyacım olan şey için çalışıyor)

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

    if (data[type] === undefined || data.length === 0) {
        return false;
    }
    return true;
};

-2

JQuery'nin çözümü var :

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

eşdeğer:

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

1
Cevabınız soru ile ilgili değil. Soru, işlevi öğenin etkinliğine yalnızca bir kez bağlamakla ilgili.
Michal Zalewski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.