jQuery натискання подій кілька разів


283

Я намагаюся написати відео-покер у Javascript як спосіб знизити основи, і я зіткнувся з проблемою, коли обробники подій jQuery click запускають кілька разів.

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

Ось функція з обробниками подій клацання, які діють; сподіваємось, це легко зрозуміти (дайте мені знати, якщо ні, то я хочу також вдосконалитись):

/** 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 на локальні змінні гроші і ставлю всередині цієї функції, а замість цього маніпулюю, а також зміню решту мого коду, щоб це зробити. :) Якщо у вас є час, ви можете також пояснити, що запропонували ініціалізація суми робиться; це схоже на якийсь регулярний вираз, але я не можу легко зрозуміти це.
Грегорі Фаулер

@GregoryFowler - не пов'язане з вашим запитанням, але ... заява про перемикання javascript, можливо, варто переглянути.
Клейтон

2
Людина, твоя функція - розміщувати обробник кліків щоразу, коли він викликається. Якщо ви викликаєте це на кожному раунді, у другому раунді у вас є два обробники тощо. Кожен обробник виконує свою роботу і в раунді 100 ви отримуєте 100 оповіщень.
Марко Фаустінеллі

Це трапляється тому, що десь у вашому коді ви повторно пов’язуєте обробник подій, попередньо не розв’язуючи його. Дивіться це запитання для аналогічного сценарію та хорошого пояснення.
jpaugh

Відповіді:


526

Щоб переконатися, що дії, натиснуті лише одним натисканням, використовуйте це:

$(".bet").unbind().click(function() {
    //Stuff
});

31
Гаразд, де ти був учора, Роб? ;) Це саме те, що я шукав, не знаю, чому я його раніше не знаходив. Але це все-таки було блаженним маскуванням, тому що я навчився багато іншого.
Грегорі Фаулер

1
;) вибачте. Я крутив колеса на кілька днів і на цьому. Я досі шукаю логічну причину, чому це взагалі трапляється.
Роб

6
Людина після стількох речей це зробила. Крім мого випадку, я використав еквівалент: $ (".
Bet

7
Як сказано нижче в jroi_web, цей метод застарілий. Дивіться відповідь @ trolle для більш сучасного рішення.
Паскаль

Обробник кліків - це не одноразовий викид. Особливо, коли це є об'ємним "if-if-if", яке відображається у питанні. Ви повинні прикріпити його і дозволити йому виконувати свою роботу, поки сторінка не живе.
Марко Фаустінеллі

378

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

Це видалить усі обробники подій:

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

Щоб видалити лише зареєстровані обробники подій "натисніть":

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

9
це слід використати в новій версії jQuery, оскільки
відключення, вмирання

Працює як шарм! Моя функція виконувалась один раз при натисканні, потім двічі, чотири рази ... .off () перед .on () вирішила цю проблему.
jumpOnCommand

146

.one ()

Кращим варіантом буде .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
перепробувавши всі відповіді, ця єдина для мене робота.
невдача

10
Варто зауважити, якщо ви хочете, щоб ця функція була запущена лише один раз PER CLICK, але продовжувати запускати при наступних клацаннях, ця буквально запускається лише один раз протягом життя сторінки. Отже, потрібно використовувати відповідь mtl із методом off (). On ().
Дерек Хьюїтт

1
@munchschair, для цього може бути кілька причин. Кілька елементів з одним класом всередині один одного. Або обробник кліків реєструється кілька разів під час ітерації.
Шаунак 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. Це означає, що якщо, наприклад, ваша глобальна змінна була оголошена, але не ініціалізована під час надання діалогового вікна інтерфейсу jQuery UI, то вона відображатиметься як неініціалізований обробник подій діалогового інтерфейсу jQuery
UkraineTrain

1
або якщо ви призначили якійсь глобальній змінній значення 16 до надання вікна діалогу інтерфейсу jQuery, яке пізніше змінилося, скажімо, на 55, тоді 16 з’явиться для цієї глобальної змінної в обробці подій діалогового вікна діалогу jQuery, навіть якщо на той час це більше не 16. Отже, це схоже на помилку інтерфейсу jQuery, що люди повинні знати.
UkraineTrain

Для дуже конкретних ситуацій, які навіть важко описати. Це працює саме так, як ви сказали: "Працює, не впливаючи на інші події". Спасибі! :)
Томс Бунья

Для мене працює зовсім чудово. Дякую!
Somnath Pawar

18

Якщо ви викликаєте цю функцію при кожному "натисканні", то для кожного виклику додається ще одна пара обробників.

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


4
Ваша відповідь спрямовувала мене в правильному напрямку, і я багато чого навчився, але недостатньо, щоб вирішити свою проблему, використовуючи jQuery, на жаль. :) Я спробував використовувати функцію ввімкнення / вимкнення, 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
ми не знаємо, чи викликається функція "pushBetButtons" лише один раз чи кілька разів. Якщо вона викликається більше одного разу, тоді подія реєструється кілька разів, а значить, вона також запускатиметься декілька разів. Навіть якщо кнопку натиснути лише один раз .
Калпеш Попат

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, тому він викликає клацання для кожного екземпляра div.


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>

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>

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() щоб визначити, чи колись називався цей метод (на цьому об'єкті події).

Цей метод працює і для користувацьких подій, що запускаються за допомогою триггера (). Також зауважте, що це не завадить запускати інші обробники цього ж елемента.


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

.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

У моєму випадку, 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>тег, тому обидва файли приєднували обробники подій до елемента. Я видалив дублюючу декларацію, і це вирішило проблему.


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


0

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



-1

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.