jQuery单击事件触发多次


282

我试图用Java脚本编写视频扑克游戏,以降低其基础知识,但我遇到了一个问题,其中jQuery click事件处理程序多次触发。

它们被附加到用于下注的按钮上,并且在游戏过程中第一手下注时效果很好(仅触发一次);但是在秒针下注中,每次按下一个下注或下注按钮都会触发两次点击事件(因此,每次按下正确的赌注量是两次)。总体而言,在按一次下注按钮时触发单击事件的次数遵循此模式- 序列的第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和bet,并对其进行了操作,并将更改我的其余代码以进行此操作。:)如果有时间,还可以解释一下建议吗正在初始化金额;它看起来像一些正则表达式,但我不容易理解。
Gregory Fowler

@GregoryFowler-与您的问题无关,但是... javascript switch语句可能值得研究。
克莱顿2014年

2
是的,您的函数在每次调用时都会放置一个点击处理程序。如果在每个回合中调用它,则在第二回合中有两个处理程序,依此类推。每个处理程序执行其工作,并且在第100轮时,您会收到100条警报。
Marco Faustinelli 2015年

发生这种情况是因为在代码中的某处,您是在重新绑定事件处理程序而没有首先取消绑定它。有关类似情况和很好的说明,请参见此问题。
jpaugh

Answers:


527

要确保一次“仅单击”操作,请使用以下命令:

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

31
好吧,昨天你在哪儿,罗布?;)这正是我在寻找的东西,不知道为什么以前没有找到它。但这仍然是变相的祝福,因为我确实学到很多其他东西。
Gregory Fowler

1
;)对不起。我也在这上旋转了几天。我仍然在寻找一个合理的理由,说明为什么它甚至首先发生。
罗布

6
经过这么多事情的人做到了。除了我的情况外,我使用了等效项:$(“。bet”)。off()。on()
MadTurki

6
就像jroi_web在下面说的那样,此方法已被弃用。有关最新解决方案,请参见@trolle的答案。
Pascal

点击处理程序不是一次性的东西。特别是当问题中显示的if-if-if很大时。只要页面存在,您就应该附加它并让它工作。
Marco Faustinelli 2015年

377

.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 2014年

11
.off()似乎需要参数才能删除特定元素的特定处理程序,否则所有事件处理程序都将被删除。例如('body').off("click", element).on('click', element, function() { //code continues。我不确定这是否是因为我先使用$(body)了元素,但是对于这种情况,off()似乎需要使用参数来专门针对手边的元素。
nicholaswmin 2015年

1
@NicholasKyriakides:是的。该文档指出:“不带参数调用.off()会删除所有附加到元素的处理程序。可以通过提供事件名称,名称空间,选择器或处理程序函数名称的组合来删除元素上的特定事件处理程序。”
mtl 2015年

1
抱歉!我不知不觉地否决了这个答案。我将尽快撤消,不知道它是怎么发生的
Daniel Brady

1
@DesAdams,不用担心:)。感谢您让我知道这是一个错误。
mtl

145

。一()

更好的选择是.one()

每个事件类型的每个元素最多只能执行一次处理程序。

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

如果有多个班级,并且每个班级需要单击一次,

$(".bet").on('click',function() {
    //Your function
    $(this).off('click');   //or $(this).unbind()
});

8
最好的答案,你从一个愚蠢的黑客救了我,这是好得多,因为它,如果你有多个onclick事件相同的元素,但在不同的地点,这将不会影响休息,而unbind()off()只会破坏等onclick又在发感谢你
Fanckush

3
尝试所有答案后,这是我唯一的一项工作。
没想到

9
需要注意的一件事是,如果您希望该功能在每次点击时仅触发一次,而在以后的点击中继续触发,则在整个页面的生命周期中,这实际上只会触发一次。因此,将需要使用带有off()。on()方法的mtl答案。
Derek Hewitt

1
@munchschair,可能有多种原因。彼此内部具有相同类的多个元素。或者单击处理程序在迭代中多次注册。
Shaunak D

2
对我不起作用-仍然多次单击触发-没有任何意义,因为只有1个具有ID的按钮元素,并且仅捕获了1个事件(click事件)。我以为这是一个jQuery错误,但它可能是Bootstrap模态对话框错误。
MC9000

78

如果发现.off().unbind()或.stopPropagation()仍无法解决您的特定问题,请尝试使用.stopImmediatePropagation()在仅希望事件处理而不会冒泡且没有发生故障的情况下效果很好影响已经处理的任何其他事件。就像是:

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

绝招!


这不仅帮助我阻止了事件两次触发,而且还使我陷入困境。事实证明,如果您在附加到jQuery UI对话框字段的事件处理程序中使用event.stopImmediatePropagation(),则由于某种原因,该对话框将不再引用全局变量的最新实例,而是使用全局变量的版本在jQuery UI对话框渲染之前。意思是,例如,如果您声明了全局变量,但未在jQuery UI对话框渲染时进行初始化,则它将显示为未初始化的jQuery UI对话框事件处理程序
UkraineTrain

1
或者,如果您在呈现jQuery UI对话框之前为某个全局变量分配了16的值,后来又更改为55,则在jQuery UI对话框事件处理程序中该全局变量将显示16,即使当时是不再是16。因此,它看起来像是人们应该注意的jQuery UI错误。
乌克兰火车

对于非常具体的情况,甚至很难描述。它的工作原理与您所说的完全一样:“在不影响任何其他事件的情况下工作”。谢谢!:)
汤姆斯·布格纳

对我来说完全正常。谢谢!
Somnath Pawar

18

如果您在每次“点击”时都调用该函数,那么它将在每次调用时添加另一对处理程序。

使用jQuery添加处理程序与设置“ onclick”属性的值不同。可以根据需要添加任意数量的处理程序。


4
您的回答使我朝着正确的方向前进,我学到了很多东西,但不幸的是,不足以解决使用jQuery的问题。:)我尝试使用开/关,实时(和委托)/ die和一个,然后使用addEventListener / removeEventListener,但是我能做的最好的事情是减慢处理程序的指数增长。我最终只是暂时用onclick解决了我的问题。但是我仍然从您的答案中学到了很多有关Javascript事件模型的知识,例如捕获/冒泡,所以我对此表示赞赏。谢谢。:)
Gregory Fowler

非常感谢,我不敢相信我不知道!
Lenny

12

一个事件将被多次注册(即使向同一处理程序注册)也会多次触发。

例如 $("ctrl").on('click', somefunction)如果每次页面部分刷新时都执行这段代码,那么每次也会注册该事件。因此,即使仅单击一次ctrl,它也可以多次执行“某些功能”-它执行的次数将取决于其注册的次数。

对于在javascript中注册的任何事件,都是如此。

解:

确保只调用一次“ on”。

出于某种原因,如果您无法控制架构,请执行以下操作:

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


你到底想说什么
Somnath Kharat 2015年

那不能解释在这种情况下发生了什么。该按钮只有一个唯一的ID,只有一个事件(不会被多次调用,因为只能单击一次)。这是一个仅出现在jQuery对话框中的错误(并且非常容易重现)。
MC9000

2
我们不知道函数“ pushingBetButtons”是仅被调用一次还是多次。.如果调用多次,则该事件将被多次注册,因此也会触发多次..即使仅单击一次按钮。
Kalpesh Popat

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

您会注意到我在html中有两次相同的类“ myclass”,因此它为div的每个实例调用click。


3

更好的选择是使用off

<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
这样可以解决用户实际单击多次的情况,但是...我得到了具有相同时间戳的多个click事件。有没有办法,我可以点击在同一毫秒3次,即使我是非常在它快速的(不知何故不知道我有多少次按下按钮)。
jpaugh

2

发生这种情况是由于特定事件多次绑定到同一元素。

对我有用的解决方案是:

使用以下命令杀死所有附加事件 .die()方法。

然后附加您的方法侦听器。

从而,

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

应该:

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

2

我们必须stopPropagation()为了避免Clicks触发事件太多次。

$(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 one确保您的事件处理程序仅被调用一次。此外,您可以为事件处理程序预订一个事件处理程序,以在完成当前单击操作的处理后允许其他单击。

<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>标签中两次在页面上加载了相同的文件,因此这两个文件都将事件处理程序附加到元素上。我删除了重复的声明,并解决了该问题。


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 }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.