Zdarzenia kliknięcia jQuery uruchamiane wielokrotnie

283

Próbuję napisać grę w pokera wideo w Javascripcie jako sposób na zapoznanie się z jej podstawami i napotkałem problem polegający na tym, że programy obsługi zdarzeń kliknięcia jQuery uruchamiają się wielokrotnie.

Są one przymocowane do przycisków służących do obstawiania i działa dobrze przy obstawianiu pierwszego rozdania podczas gry (strzelanie tylko raz); ale w zakładach na drugie rozdanie uruchamia zdarzenie kliknięcia dwa razy za każdym razem, gdy zostanie naciśnięty przycisk zakładu lub postawienia zakładu (więc dwukrotność prawidłowej kwoty za każde naciśnięcie). Ogólnie rzecz biorąc, następuje to według tego, ile razy zdarzenie kliknięcia jest uruchamiane po jednokrotnym naciśnięciu przycisku zakładu - gdzie i-ty termin sekwencji dotyczy zakładów i-tego rozdania od początku gry: 1, 2, 4 , 7, 11, 16, 22, 29, 37, 46, który wydaje się być n (n + 1) / 2 + 1 dla tego, co jest warte - i nie byłem wystarczająco inteligentny, aby to zrozumieć, użyłem OEIS . :)

Oto funkcja z działającymi funkcjami obsługi zdarzeń kliknięcia; miejmy nadzieję, że łatwo to zrozumieć (daj mi znać, jeśli nie, chcę też być lepszy):

/** The following function keeps track of bet buttons that are pressed, until place button is pressed to place bet. **/
function pushingBetButtons() {
    $("#money").text("Money left: $" + player.money); // displays money player has left

    $(".bet").click(function() {
        var amount = 0; // holds the amount of money the player bet on this click
        if($(this).attr("id") == "bet1") { // the player just bet $1
            amount = 1;
        } else if($(this).attr("id") == "bet5") { // etc.
            amount = 5;
        } else if($(this).attr("id") == "bet25") {
            amount = 25;
        } else if($(this).attr("id") == "bet100") {
            amount = 100;
        } else if($(this).attr("id") == "bet500") {
            amount = 500;
        } else if($(this).attr("id") == "bet1000") {
            amount = 1000;
        }
        if(player.money >= amount) { // check whether the player has this much to bet
            player.bet += amount; // add what was just bet by clicking that button to the total bet on this hand
            player.money -= amount; // and, of course, subtract it from player's current pot
            $("#money").text("Money left: $" + player.money); // then redisplay what the player has left
        } else {
            alert("You don't have $" + amount + " to bet.");
        }
    });

    $("#place").click(function() {
        if(player.bet == 0) { // player didn't bet anything on this hand
            alert("Please place a bet first.");
        } else {
            $("#card_para").css("display", "block"); // now show the cards
            $(".card").bind("click", cardClicked); // and set up the event handler for the cards
            $("#bet_buttons_para").css("display", "none"); // hide the bet buttons and place bet button
            $("#redraw").css("display", "block"); // and reshow the button for redrawing the hand
            player.bet = 0; // reset the bet for betting on the next hand
            drawNewHand(); // draw the cards
        }
    });
}

Daj mi znać, jeśli masz jakieś pomysły lub sugestie lub jeśli rozwiązanie mojego problemu jest podobne do rozwiązania innego problemu tutaj (przejrzałem wiele podobnie utytułowanych wątków i nie miałem szczęścia znaleźć rozwiązania, które mogłoby zadziałać dla mnie).

Gregory Fowler
źródło
var amount = parseInt(this.id.replace(/[^\d]/g,''),10);A jeśli zamierzasz użyć tej samej właściwości elementu więcej niż raz, buforuj tę właściwość, nie szukaj dalej. Wyszukiwanie jest drogie.
David mówi, że przywróć Monikę
Dziękujemy za odpowiedź i wskazówkę dotyczącą właściwości buforowania. Ustawiam player.money i player.bet na zmienne lokalne pieniądze i stawiam zakłady wewnątrz tej funkcji i manipuluję nimi, a także zmienię resztę mojego kodu, aby to zrobić. :) Jeśli masz czas, czy możesz również wyjaśnić, co sugerujesz robi się inicjalizacja kwoty; wygląda na jakieś wyrażenie regularne, ale nie mogę tego łatwo zrozumieć.
Gregory Fowler,
@GregoryFowler - niezwiązany z twoim pytaniem, ale ... warto sprawdzić instrukcję zmiany javascript.
Clayton
2
Człowieku, twoja funkcja umieszcza moduł obsługi kliknięć przy każdym wywołaniu. Jeśli wywołujesz go w każdej rundzie, w drugiej rundzie masz dwóch przewodników i tak dalej. Każdy przewodnik wykonuje swoją pracę, aw rundzie 100 otrzymujesz 100 alertów.
Marco Faustinelli
Dzieje się tak, ponieważ gdzieś w kodzie ponownie łączysz moduł obsługi zdarzeń bez uprzedniego rozpięcia. Zobacz to pytanie, aby uzyskać podobny scenariusz i dobre wyjaśnienie.
jpaugh

Odpowiedzi:

526

Aby mieć pewność, że działania wykonywane tylko za jednym kliknięciem, użyj tego:

$(".bet").unbind().click(function() {
    //Stuff
});
Obrabować
źródło
31
OK, gdzie byłeś wczoraj, Rob? ;) Właśnie tego szukałem, nie wiem, dlaczego wcześniej tego nie znalazłem. Ale wciąż było to błogosławieństwo w przebraniu, ponieważ nauczyłem się wielu innych rzeczy.
Gregory Fowler
1
;) przepraszam. Odwróciłem też kilka kół na tym jednym. Wciąż szukam logicznego powodu, dlaczego w ogóle tak się dzieje.
Rob
6
Człowiek po tylu rzeczach to zrobił. Z wyjątkiem mojego przypadku użyłem ekwiwalentu: $ (".
Bet
7
Jak mówi jroi_web poniżej, ta metoda jest przestarzała. Zobacz odpowiedź @ trolle na nowsze rozwiązanie.
Pascal
Moduł obsługi kliknięć nie jest jednorazową rzeczą jednorazową. Zwłaszcza, gdy jest to nieporęczne, jeśli-jeśli-jeśli pokazano w pytaniu. Musisz go załączyć i pozwolić mu działać tak długo, jak długo strona będzie żyła.
Marco Faustinelli,
378

.unbind()jest przestarzałe i .off()zamiast tego należy użyć tej metody. Po prostu zadzwoń, .off()zanim zadzwonisz .on().

Spowoduje to usunięcie wszystkich procedur obsługi zdarzeń:

$(element).off().on('click', function() {
    // function body
});

Aby usunąć tylko zarejestrowane programy obsługi zdarzeń „kliknięcia”:

$(element).off('click').on('click', function() {
    // function body
});
mtl
źródło
9
należy tego użyć w nowszej wersji jQuery, ponieważ unbind, die lub live jest już przestarzałe
jroi_web
Działa jak marzenie! Moja funkcja działałaby raz na kliknięcie, a następnie dwa, cztery razy ... .off () zanim .on () rozwiązał ten problem.
jumpOnCommand
146

.jeden()

Lepszą opcją byłoby .one():

Procedura obsługi jest wykonywana najwyżej raz dla elementu na typ zdarzenia.

$(".bet").one('click',function() {
    //Your function
});

W przypadku wielu klas i każdej klasy należy kliknąć raz,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});
Shaunak D.
źródło
8
Najlepszą odpowiedzią, uratowałeś mnie z głupią siekać, jest to o wiele lepiej, bo jeśli masz wiele onclickzdarzeń do tego samego elementu, ale w różnych miejscach to nie wpłynie na odpoczynek, podczas unbind()i off()po prostu niszczą inne onclicks dziękuję jeszcze raz
Fanckush
3
po wypróbowaniu wszystkich odpowiedzi ta praca jest dla mnie jedyna.
neversion
10
Należy zwrócić uwagę na jedno: jeśli chcesz, aby funkcja była uruchamiana tylko raz NA KLIKNIĘCIE, ale nadal była uruchamiana przy kolejnych kliknięciach, ta dosłownie uruchomi się tylko raz na cały okres użytkowania strony. Więc odpowiedź mtl z metodą off (). On () będzie musiała zostać użyta.
Derek Hewitt
1
@munchschair, może być wiele przyczyn tego. Wiele elementów o tej samej klasie wewnątrz siebie. Lub moduł obsługi kliknięć jest rejestrowany wielokrotnie w iteracji.
Shaunak D
3
Nie działa dla mnie - nadal wielokrotne uruchamianie kliknięć - nie ma sensu, ponieważ jest tylko 1 element przycisku z identyfikatorem i tylko 1 zdarzenie jest uwięzione (zdarzenie kliknięcia). Myślałem, że to błąd jQuery, ale prawdopodobnie jest to błąd modalnego okna dialogowego Bootstrap.
MC9000,
78

Jeśli stwierdzisz, że .off () .unbind () lub .stopPropagation () nadal nie rozwiązuje konkretnego problemu, spróbuj użyć .stopImmediatePropagation () działa świetnie w sytuacjach, gdy chcesz, aby Twoje zdarzenie było obsługiwane bez propagacji i bez wpływające na inne obsługiwane już zdarzenia. Coś jak:

$(".bet").click(function(event) {
  event.stopImmediatePropagation();
  //Do Stuff
});

Zrób sztuczkę!

Alfonse Pinto
źródło
To pomogło mi, zarówno jeśli chodzi o powstrzymanie zdarzenia przed dwukrotnym odpaleniem, ale także pomieszało mi dużo czasu. Okazuje się, że jeśli użyjesz event.stopImmediatePropagation () w procedurze obsługi zdarzeń dołączonej do pola okna dialogowego interfejsu użytkownika jQuery, to z jakiegoś powodu okno dialogowe nie może już odwoływać się do najnowszej instancji zmiennych globalnych i zamiast tego używa wersji zmiennych globalnych przed renderowaniem okna dialogowego interfejsu użytkownika jQuery. Oznacza to, że jeśli na przykład twoja zmienna globalna została zadeklarowana, ale nie została zainicjowana podczas renderowania okna dialogowego interfejsu użytkownika jQuery, to pojawi się jako niezainicjowana obsługa zdarzeń
okna
1
lub jeśli przypisałeś do jakiejś zmiennej globalnej wartość 16 przed renderowaniem okna dialogowego interfejsu użytkownika jQuery, który później zmienił się, powiedzmy, na 55, wtedy 16 pojawi się dla tej zmiennej globalnej w module obsługi zdarzeń jQuery UI, nawet jeśli w tym czasie już nie 16. Wygląda to na błąd interfejsu użytkownika jQuery, o którym ludzie powinni wiedzieć.
UkraineTrain
Do bardzo specyficznych sytuacji, które są nawet trudne do opisania. Działa dokładnie tak, jak powiedziałeś: „Działa bez wpływu na inne zdarzenia”. Dziękuję Ci! :)
Toms Bugna
Działa dla mnie całkowicie dobrze. Dzięki!
Somnath Pawar
18

Jeśli wywołujesz tę funkcję przy każdym „kliknięciu”, to dodaje kolejną parę procedur obsługi dla każdego połączenia.

Dodanie modułów obsługi za pomocą jQuery po prostu nie przypomina ustawiania wartości atrybutu „onclick”. Można dodać dowolną liczbę programów obsługi.

Pointy
źródło
4
Twoja odpowiedź skierowała mnie we właściwym kierunku i wiele się nauczyłem, ale nie dość, aby rozwiązać mój problem za pomocą jQuery. :) Próbowałem użyć opcji on / off, live (i delegate) / die, a następnie addEventListener / removeEventListener, ale najlepsze, co mogłem zrobić, to spowolnić gwałtowny wzrost modułów obsługi. W końcu na razie rozwiązałem problem z kliknięciem. Ale nadal wiele się nauczyłem o modelu zdarzeń Javascript, takim jak przechwytywanie / propagowanie, z twojej odpowiedzi, więc doceniam to. Dziękuję Ci. :)
Gregory Fowler,
Dziękuję bardzo, nie mogę uwierzyć, że tego nie wiedziałem!
Lenny
12

wydarzenie zostanie uruchomione wiele razy, jeśli zostanie zarejestrowane wielokrotnie (nawet jeśli dotyczy tego samego modułu obsługi).

np. $("ctrl").on('click', somefunction)jeśli ten fragment kodu jest wykonywany za każdym razem, gdy strona jest częściowo odświeżana, zdarzenie jest również rejestrowane za każdym razem. Dlatego nawet jeśli klikniesz ctrl tylko raz, może on wykonać wielokrotnie „jakąś funkcję” - ile razy wykona, będzie zależeć od tego, ile razy został zarejestrowany.

dotyczy to każdego zdarzenia zarejestrowanego w javascript.

rozwiązanie:

pamiętaj, aby zadzwonić tylko raz.

i z jakiegoś powodu, jeśli nie możesz kontrolować architektury, zrób to:

$("ctrl").off('click'); $("ctrl").on('click', somefunction);

Kalpesh Popat
źródło
Co naprawdę chcesz powiedzieć?
Somnath Kharat
To nie wyjaśnia, co się dzieje w tym przypadku. Przycisk ma jeden unikalny identyfikator i tylko 1 zdarzenie (NIE jest wywoływany wiele razy, ponieważ można go kliknąć tylko raz). Jest to błąd, który pojawia się tylko w oknach dialogowych jQuery (i bardzo łatwo go odtworzyć).
MC9000,
2
nie wiemy, czy funkcja „pushingBetButtons” jest wywoływana tylko raz, czy wiele razy .. jeśli zostanie wywołana więcej niż raz, to zdarzenie jest rejestrowane wiele razy, a zatem wywoła również wiele razy .. nawet jeśli przycisk zostanie kliknięty tylko raz .
Kalpesh Popat
4

Miałem problem z powodu znaczników.

HTML:

<div class="myclass">
 <div class="inner">

  <div class="myclass">
   <a href="#">Click Me</a>
  </div>

 </div>
</div>

jQuery

$('.myclass').on('click', 'a', function(event) { ... } );

Zauważysz, że mam dwukrotnie tę samą klasę „myclass” w html, więc wywołuje kliknięcie dla każdej instancji div.

Bobz
źródło
3

Lepszą opcją byłoby wyłączenie

<script>
function flash() {
  $("div").show().fadeOut("slow");
}
$("#bind").click(function() {
  $( "body" )
    .on("click", "#theone", flash)
    .find("#theone")
      .text("Can Click!");
});
$("#unbind").click(function() {
  $("body")
    .off("click", "#theone", flash)
    .find("#theone")
      .text("Does nothing...");
});
</script>
arc_shiva
źródło
3

Wszystkie rzeczy związane z .on () i .one () są świetne, a jquery jest świetne.

Ale czasami chcesz, aby było trochę bardziej oczywiste, że użytkownik nie może kliknąć, aw takich przypadkach możesz zrobić coś takiego:

function funName(){
    $("#orderButton").prop("disabled", true);
    //  do a bunch of stuff
    // and now that you're all done
    setTimeout(function(){
        $("#orderButton").prop("disabled",false);
        $("#orderButton").blur();
    }, 3000);
}

a twój przycisk wyglądałby tak:

<button onclick='funName()'>Click here</button>
rikkitikkitumbo
źródło
1
To rozwiązałoby problem faktycznego kliknięcia przez użytkownika wiele razy, ale ... Otrzymuję wiele zdarzeń kliknięcia z tym samym znacznikiem czasu. Nie ma mowy, żebym mógł kliknąć 3 razy w tej samej milisekundie, nawet gdybym był bardzo szybki (i jakoś nieświadomy, ile razy naciskam przycisk).
jpaugh
2

Dzieje się tak, ponieważ dane zdarzenie jest wielokrotnie powiązane z tym samym elementem.

Rozwiązaniem, które działało dla mnie jest:

Zabij wszystkie zdarzenia dołączone za pomocą .die()metody.

A następnie dołącz swój detektor metod.

A zatem,

$('.arrow').click(function() {
// FUNCTION BODY HERE
}

Powinien być:

$('.arrow').die("click")
$('.arrow').click(function() {
// FUNCTION BODY HERE
}
Uczeń
źródło
2

Musimy stopPropagation(), aby zbyt wiele razy unikać zdarzenia uruchamiającego kliknięcia.

$(this).find('#cameraImageView').on('click', function(evt) {
   evt.stopPropagation();
   console.log("Camera click event.");
});

Zapobiega to propagacji zdarzenia w drzewie DOM, zapobiegając powiadamianiu o zdarzeniu przez wszystkie programy nadrzędne. Ta metoda nie przyjmuje żadnych argumentów.

Możemy użyć event.isPropagationStopped()do ustalenia, czy ta metoda została kiedykolwiek wywołana (dla tego obiektu zdarzenia).

Ta metoda działa również w przypadku niestandardowych zdarzeń wyzwalanych za pomocą trigger (). Należy pamiętać, że nie zapobiegnie to uruchomieniu innych procedur obsługi tego samego elementu.

Daniel Raja Singh
źródło
2

W moim przypadku korzystałem z funkcji „deleguj”, więc żadne z tych rozwiązań nie działało. Wydaje mi się, że to przycisk pojawiający się wiele razy za pośrednictwem wywołań ajax powodował problem z wieloma kliknięciami. Rozwiązania wykorzystywały limit czasu, więc rozpoznawane jest tylko ostatnie kliknięcie:

var t;
$('body').delegate( '.mybutton', 'click', function(){
    // clear the timeout
    clearTimeout(t);
    // Delay the actionable script by 500ms
    t = setTimeout( function(){
        // do something here
    },500)
})
Trevor Lettman
źródło
1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}
Rajan Rajan M.
źródło
1

.one strzela tylko raz przez cały czas istnienia strony

Jeśli więc chcesz przeprowadzić walidację, nie jest to właściwe rozwiązanie, ponieważ jeśli nie opuścisz strony po walidacji, nigdy nie wrócisz. Lepszy w użyciu

$(".bet").on('click',function() 
{ //validation 
   if (validated) { 
      $(".bet").off('click'); //prevent to fire again when we are not yet off the page
      //go somewhere
    }
});
Pieter van Kampen
źródło
1

Kiedy rozwiązuję ten problem, zawsze używam:

$(".bet").unbind("click").bind("click", function (e) {
  // code goes here
}

W ten sposób rozpinam i ponownie wiążę jednym pociągnięciem.

Andy
źródło
0

https://jsfiddle.net/0vgchj9n/1/

Aby mieć pewność, że zdarzenie zawsze uruchamia się tylko raz, możesz użyć Jquery .one (). JQuery one zapewnia, że ​​moduł obsługi zdarzeń wywoływany jest tylko raz. Ponadto możesz zasubskrybować moduł obsługi zdarzeń za pomocą jednego, aby umożliwić dalsze kliknięcia po zakończeniu przetwarzania bieżącej operacji kliknięcia.

<div id="testDiv">
  <button class="testClass">Test Button</button>
</div>

var subscribeClickEvent = function() {$("#testDiv").one("click", ".testClass", clickHandler);};

function clickHandler() {
  //... perform the tasks  
  alert("you clicked the button");
  //... subscribe the click handler again when the processing of current click operation is complete  
  subscribeClickEvent();
}

subscribeClickEvent();
Razan Paul
źródło
0

Spróbuj w ten sposób:

<a href="javascript:void(0)" onclick="this.onclick = false; fireThisFunctionOnlyOnce()"> Fire function </a>
Adam Kozłowski
źródło
0

W moim przypadku zdarzenie onclick było uruchamiane wiele razy, ponieważ stworzyłem ogólną procedurę obsługi zdarzeń podobnie jak

  `$('div').on("click", 'a[data-toggle="tab"]',function () {
        console.log("dynamic bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
   });`

zmienić na

    `$('div#mainData').on("click", 'a[data-toggle="tab"]',function () {
        console.log("dynamic bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
    });`

a także muszą tworzyć osobne procedury obsługi dla kliknięć statycznych i dynamicznych, dla statycznych kliknięć kart

    `$('a[data-toggle="tab"]').on("click",function () {
        console.log("static bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
    });`
Awais Nasir
źródło
0

W moim przypadku *.jsdwukrotnie załadowałem ten sam plik na stronę w <script>tagu, więc oba pliki dołączały procedury obsługi zdarzeń do elementu. Usunąłem duplikat deklaracji i to rozwiązało problem.

inostia
źródło
0

Innym rozwiązaniem, które znalazłem, było to, że jeśli masz wiele klas i masz do czynienia z przyciskami opcji podczas klikania etykiety.

$('.btn').on('click', function(e) {
    e.preventDefault();

    // Hack - Stop Double click on Radio Buttons
    if (e.target.tagName != 'INPUT') {
        // Not a input, check to see if we have a radio
        $(this).find('input').attr('checked', 'checked').change();
    }
});
Andrew Vink
źródło
0

Miałem ten problem z linkiem generowanym dynamicznie:

$(document).on('click', '#mylink', function({...do stuff...});

Znalazłem wymianie documentz 'body'Naprawiono problem dla mnie:

$('body').on('click', '#mylink', function({...do stuff...});

nateM
źródło
0

Unbind () działa, ale może to powodować inne problemy w przyszłości. Program obsługi uruchamia się wiele razy, gdy znajduje się w innym module obsługi, więc trzymaj swój moduł obsługi na zewnątrz, a jeśli chcesz wartości modułu obsługi, który się zagnieździł, przypisz je do zmiennej globalnej, która będzie dostępna dla twojego modułu obsługi.

praveen poonja
źródło
0

Jeśli to działa dobrze

$( "#ok" ).bind( "click", function() {
    console.log("click"); 
});
Santosh Vishwakarma
źródło
-1

Poniższy kod działał dla mnie w mojej aplikacji czatu do obsługi wielu zdarzeń wyzwalających kliknięcie myszą więcej niż raz. if (!e.originalEvent.detail || e.originalEvent.detail == 1) { // Your code logic }

użytkownik2792303
źródło