События нажатия jQuery запускаются несколько раз

284

Я пытаюсь написать видео покер на Javascript, чтобы разобраться в его основах, и столкнулся с проблемой, когда обработчики событий нажатия jQuery запускаются несколько раз.

Они прикреплены к кнопкам для размещения ставки, и это прекрасно работает для размещения ставки на первую руку во время игры (стрельба только один раз); но при ставке на вторую руку она запускает событие нажатия дважды каждый раз, когда нажимается кнопка ставки или ставка на ставку (таким образом, в два раза правильная сумма ставится для каждого нажатия). В целом, этот шаблон повторяется для количества раз, когда событие нажатия срабатывает при однократном нажатии кнопки ставки - где i-й член последовательности относится к ставке на i-ю руку с начала игры: 1, 2, 4 , 7, 11, 16, 22, 29, 37, 46, что, по-видимому, равно n (n + 1) / 2 + 1 для чего бы то ни было - и я не был достаточно умен, чтобы понять это, я использовал OEIS , :)

Вот функция с обработчиками событий click, которые работают; надеюсь, это легко понять (дайте мне знать, если нет, я тоже хочу стать лучше):

/** 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
        }
    });
}

Пожалуйста, дайте мне знать, если у вас есть какие-либо идеи или предложения, или если решение моей проблемы похоже на решение другой проблемы здесь (я просмотрел много одноименных веток и не смог найти решение, которое могло бы работать для меня).

Грегори Фаулер
источник
var amount = parseInt(this.id.replace(/[^\d]/g,''),10);И если вы собираетесь использовать одно и то же свойство элемента более одного раза, кэшируйте это свойство, не ищите его. Поиск дорог.
Дэвид говорит восстановить Монику
Спасибо за ответ и совет по кешированию свойств. Я установил для player.money и player.bet локальные переменные money и сделал ставку внутри этой функции, и вместо этого манипулировал ими, а также изменю оставшуюся часть моего кода, чтобы сделать это тоже :) выполняется инициализация суммы; это выглядит как регулярное выражение, но я не могу понять это легко.
Грегори Фаулер
@GregoryFowler - не имеет отношения к вашему вопросу, но ... возможно, стоит рассмотреть инструкцию переключения javascript.
Клейтон
2
Человек, ваша функция помещает обработчик щелчка каждый раз, когда он вызывается. Если вы вызываете его в каждом раунде, во втором раунде у вас есть два обработчика и так далее. Каждый обработчик выполняет свою работу, и на 100 раунде вы получаете 100 предупреждений.
Марко Фаустинелли
Это происходит потому, что где-то в вашем коде вы перепривязываете обработчик событий, не отсоединив его сначала. Посмотрите этот вопрос для аналогичного сценария и хорошего объяснения.
17

Ответы:

528

Чтобы убедиться, что только клик действия, используйте это:

$(".bet").unbind().click(function() {
    //Stuff
});
обкрадывать
источник
31
Хорошо, где ты был вчера, Роб? ;) Это именно то, что я искал, не знаю, почему я не нашел его раньше. Но это было скрытое благословение, потому что я узнал много других вещей.
Грегори Фаулер
1
;) Прости. На этом я тоже несколько дней крутил колеса. Я все еще ищу логическую причину того, почему это вообще происходит вообще.
Роб
6
Человек после стольких вещей это сделал. За исключением моего случая, я использовал эквивалент: $ (". Bet"). Off ().
On
7
Как сказано ниже в jroi_web, этот метод устарел. Смотрите ответ @ trolle для более свежего решения.
Паскаль
Обработчик щелчков не одноразовая вещь. Особенно, когда это громоздко, если-если-если показано в вопросе. Вы должны прикрепить его и позволить ему делать свою работу, пока страница живет.
Марко Фаустинелли
380

.unbind()устарела, и вы должны использовать .off()метод вместо. Просто позвоните .off()прямо перед звонком.on() .

Это удалит все обработчики событий:

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

Чтобы удалить только зарегистрированные обработчики событий click

$(element).off('click').on('click', function() {
    // function body
});
средний уровень прилива
источник
9
это следует использовать в более новой версии jQuery, поскольку unbind, die или live уже устарели
jroi_web
Работает как шарм! Моя функция будет запускаться один раз при нажатии, затем дважды, четыре раза ... .off (), прежде чем .on () решит эту проблему.
jumpOnCommand
146

.один()

Лучшим вариантом будет .one():

Обработчик выполняется не более одного раза для каждого элемента для каждого типа события.

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

В случае нескольких классов, и каждый класс нужно нажать один раз,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});
Шаунак Д
источник
8
Лучший ответ, вы спасли меня от глупого хака, это гораздо лучше , потому что если у вас есть несколько onclickсобытий в тот же элемент , но в разных местах , это не повлияет на остальное, в то время как unbind()и off()просто уничтожить другие onclicks раз спасибо
Fanckush
3
перепробовав все ответы, эта работа для меня единственная.
Neversion
10
Стоит отметить, что если вы хотите, чтобы функция срабатывала только один раз за клик, но продолжала срабатывать при последующих щелчках, эта функция будет срабатывать буквально только один раз за всю жизнь страницы. Так что ответ mtl с методом off (). On () нужно будет использовать.
Дерек Хьюитт
1
@munchschair, для этого может быть несколько причин. Несколько элементов одного класса внутри друг друга. Или обработчик кликов регистрируется несколько раз за итерацию.
Shaunak D
3
Не работает для меня - все еще многократное нажатие - не имеет смысла, так как есть только 1 элемент кнопки с идентификатором, и только 1 событие перехвачено (событие нажатия). Я думал, что это ошибка jQuery, но это, вероятно, ошибка модального диалога Bootstrap.
MC9000
78

Если вы обнаружите, что .off () .unbind () или .stopPropagation () по-прежнему не решают вашу конкретную проблему, попробуйте использовать .stopImmediatePropagation (). Отлично работает в ситуациях, когда вы просто хотите, чтобы ваше событие обрабатывалось без каких-либо пузырей и без выполнение любых других событий, которые уже обрабатываются. Что-то вроде:

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

делает трюк!

Альфонс Пинто
источник
Это помогло мне так же, как остановка события от стрельбы дважды, но также и запутало меня. Оказывается, что если вы используете event.stopImmediatePropagation () в обработчике событий, прикрепленном к полю диалога jQuery UI, то по какой-то причине диалог больше не может ссылаться на новейший экземпляр глобальных переменных, а вместо этого использует версию глобальных переменных. до рендеринга диалогового окна jQuery UI. Это означает, что если, например, ваша глобальная переменная была объявлена, но не инициализирована во время рендеринга диалога jQuery UI, то она будет отображаться как неинициализированный обработчик события диалога jQuery UI
UkraineTrain
1
или если вы присвоили какой-то глобальной переменной значение 16 до рендеринга jQuery UI Dialog, который позже изменился, скажем, на 55, то 16 будет отображаться для этой глобальной переменной в обработчике событий JQuery UI Dialog, даже если в то время это не больше 16. Итак, это похоже на ошибку jQuery UI, о которой должны знать люди.
UkraineTrain
Для очень специфических ситуаций, которые даже сложно описать. Он работает точно так, как Вы сказали: «Работает, не затрагивая другие события». Спасибо! :)
Томс Бугна
Работает совершенно нормально для меня. Спасибо!
Сомнатх Павар
18

Если вы вызываете эту функцию при каждом «щелчке», то при каждом вызове она добавляет другую пару обработчиков.

Добавление обработчиков с помощью jQuery не похоже на установку значения атрибута «onclick». Можно добавить столько обработчиков, сколько пожелаете.

Заостренный
источник
4
Ваш ответ направил меня в правильном направлении, и я многому научился, но, к сожалению, этого недостаточно, чтобы решить мою проблему с помощью jQuery. :) Я пытался использовать on / off, live (и делегировать) / die и one, а затем addEventListener / removeEventListener, но лучшее, что я мог сделать, - это замедлить экспоненциальный рост обработчиков. В конце концов я только что решил свою проблему с помощью onclick. Но я все еще многое узнал о модели событий Javascript, например, о захвате / всплытии, из вашего ответа, поэтому я ценю это. Спасибо. :)
Грегори Фаулер
Большое спасибо, я не могу поверить, что я не знал этого!
Ленни
12

Событие будет срабатывать несколько раз, когда оно будет зарегистрировано несколько раз (даже если один и тот же обработчик).

например $("ctrl").on('click', somefunction) если этот фрагмент кода выполняется каждый раз, когда страница частично обновляется, событие также регистрируется каждый раз. Следовательно, даже если щелкнуть Ctrl только один раз, он может выполнить «некоторую функцию» несколько раз - сколько раз он будет выполняться, будет зависеть от того, сколько раз он был зарегистрирован.

это верно для любого события, зарегистрированного в JavaScript.

решение:

убедитесь, что вы звоните только один раз.

и по какой-то причине, если вы не можете контролировать архитектуру, сделайте это:

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

Калпеш Попат
источник
Что ты действительно хочешь сказать?
Сомнатх Харат
Это не объясняет, что происходит в этом случае. Кнопка имеет один уникальный идентификатор и только 1 событие (она НЕ вызывается несколько раз, поскольку ее можно нажать только один раз). Это ошибка, которая появляется только в диалоговых окнах jQuery (и очень легко воспроизводится).
MC9000
2
мы не знаем, вызывается ли функция «pushingBetButtons» только один раз или несколько раз .. если она вызывается более одного раза, то событие регистрируется несколько раз и, следовательно, оно также будет запускаться несколько раз… даже если кнопка нажата только один раз ,
Калпеш Попат
4

У меня была проблема из-за разметки.

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

Вы заметили, что у меня есть один и тот же класс «myclass» дважды в html, поэтому он вызывает click для каждого экземпляра div.

Bobz
источник
3

Лучшим вариантом будет использование выключения

<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
источник
3

Все, что касается .on () и .one () великолепно, а jquery великолепно.

Но иногда вы хотите, чтобы было немного более очевидно, что пользователю не разрешено нажимать, и в этих случаях вы можете сделать что-то вроде этого:

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

и ваша кнопка будет выглядеть так:

<button onclick='funName()'>Click here</button>
rikkitikkitumbo
источник
1
Это решило бы случай, когда пользователь нажимал несколько раз, но ... Я получаю несколько событий щелчков с одной и той же отметкой времени. Нет никакого способа, которым я мог бы щелкнуть 3 раза за одну и ту же миллисекунду, даже если бы я был очень быстр в этом (и каким-то образом не знал, сколько раз я нажимаю кнопку).
jpaugh
2

Это происходит из-за того, что конкретное событие несколько раз связано с одним и тем же элементом.

Решение, которое сработало для меня:

Убить все события, прикрепленные с помощью .die()метода.

А затем прикрепите свой метод слушателя.

Таким образом,

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

должно быть:

$('.arrow').die("click")
$('.arrow').click(function() {
// FUNCTION BODY HERE
}
Ученик
источник
2

Мы должны stopPropagation()Для того, чтобы избежать кликов триггеров событие слишком много раз.

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

Это предотвращает всплытие события в DOM-дереве, предотвращая уведомление любых родительских обработчиков о событии. Этот метод не принимает никаких аргументов.

Мы можем использовать, event.isPropagationStopped()чтобы определить, был ли когда-либо вызван этот метод (для этого объекта события).

Этот метод работает и для пользовательских событий, запускаемых с помощью trigger (). Обратите внимание, что это не помешает запуску других обработчиков на том же элементе.

Даниэль Раджа Сингх
источник
2

В моем случае я использовал «делегат», поэтому ни одно из этих решений не сработало. Я полагаю, что это была кнопка, появляющаяся много раз через вызовы ajax, которая вызывала проблему множественного щелчка Решения использовали тайм-аут, поэтому распознается только последний щелчок:

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)
})
Тревор Леттман
источник
1
$(element).click(function (e)
{
  if(e.timeStamp !== 0) // This will prevent event triggering more then once
   {
      //do your stuff
   }
}
Раджан Раджан М
источник
1

.one срабатывает только один раз за всю жизнь страницы

Поэтому, если вы хотите выполнить проверку, это не правильное решение, потому что, когда вы не покидаете страницу после проверки, вы никогда не вернетесь. Лучше использовать

$(".bet").on('click',function() 
{ //validation 
   if (validated) { 
      $(".bet").off('click'); //prevent to fire again when we are not yet off the page
      //go somewhere
    }
});
Питер ван Кампен
источник
1

Когда я имею дело с этой проблемой, я всегда использую:

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

Таким образом, я развязываю и переплетаю одним и тем же ходом.

Энди
источник
0

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

Чтобы событие всегда срабатывало только один раз, вы можете использовать Jquery .one (). JQuery один гарантирует, что ваш обработчик событий вызывается только один раз. Кроме того, вы можете подписать свой обработчик событий на один, чтобы разрешить дальнейшие щелчки, когда вы закончите обработку текущей операции щелчка.

<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();
Разан Пол
источник
0

Попробуйте так:

<a href="javascript:void(0)" onclick="this.onclick = false; fireThisFunctionOnlyOnce()"> Fire function </a>
Адам Козловский
источник
0

В моем случае событие onclick запускалось несколько раз, потому что я сделал общий обработчик события сравнительно как

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

изменился на

    `$('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[data-toggle="tab"]').on("click",function () {
        console.log("static bootstrap tab clicked");
        var href = $(this).attr('href');
        window.location.hash = href;
    });`
Авайс Насир
источник
0

В моем случае я загружал один и тот же *.jsфайл на странице дважды в <script>теге, поэтому оба файла прикрепляли обработчики событий к элементу. Я удалил дубликат декларации, и это решило проблему.

inostia
источник
0

Другое решение, которое я нашел, состояло в следующем: если у вас есть несколько классов и вы работаете с переключателями при нажатии на ярлык.

$('.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();
    }
});
Андрей Винк
источник
0

У меня была эта проблема с динамически сгенерированной ссылкой:

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

Я нашел замену documentс 'body'фиксированным вопросом для меня:

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

nateM
источник
0

Unbind () работает, но это может вызвать другие проблемы в будущем. Обработчик запускается несколько раз, когда он находится внутри другого обработчика, поэтому держите ваш обработчик снаружи, и, если вы хотите, чтобы значения обработчика были вложенными, присвойте их глобальной переменной, которая будет доступна вашему обработчику.

Правин Пунджа
источник
0

Если это работает нормально

$( "#ok" ).bind( "click", function() {
    console.log("click"); 
});
сантош вишвакарма
источник
-1

Приведенный ниже код работал для меня в моем приложении чата для обработки нескольких событий, вызывающих щелчок мышью, более одного раза. if (!e.originalEvent.detail || e.originalEvent.detail == 1) { // Your code logic }

user2792303
источник