【问题标题】:How to temporarily disable a click handler in jQuery?如何在 jQuery 中临时禁用点击处理程序?
【发布时间】:2010-11-18 18:45:22
【问题描述】:

假设我有类似下面的东西来捕获按钮的点击事件:

$("#button_id").click(function() {
  //disable click event
  //do something
  //re-enable click event
}

如何暂时禁用按钮的点击事件,直到原始点击的处理结束发生?我基本上在单击按钮后让 div 消失,但是如果用户快速单击按钮几次,它会在 div 有机会消失之前处理所有这些单击。我想“去抖动”按钮,以便在 div 消失之前只注册第一次点击。

【问题讨论】:

标签: javascript jquery


【解决方案1】:

我注意到这篇文章很旧,但它似乎在谷歌上排名靠前,并且从未提供过这种解决方案,所以我还是决定发布它。

您可以禁用光标事件并稍后通过 css 再次启用它们。它在所有主要浏览器上都受支持,并且在某些情况下可能会很有用。

$("#button_id").click(function() {

   $("#button_id").css("pointer-events", "none");
   //do something
   $("#button_id").css("pointer-events", "auto");
}

【讨论】:

  • 理论上这是一个很好的解决方案,但它并没有得到普遍支持:IE 支持仅从版本 11 开始。这意味着目前仍有许多浏览器需要您寻找其他解决方案。
  • 如果您仍然需要支持旧 IE 版本,有一些简单的方法可以解决该问题(您可以在 wp8 HTML5 移动应用程序中使用上面的代码,但不能在 7 中使用)。在旧版本中,我建议使用此方法修复它vinylfox.com/forwarding-mouse-events-through-layers
  • -1;像这里的其他几个答案一样,这不起作用,因为在考虑是否为下一次点击。至少在 Chrome 中(但可能在所有浏览器中),这将与没有 .css() 调用的相同代码完全相同。有关演示,请参阅jsfiddle.net/at75H/16;打开您的控制台,多次单击按钮,然后观察处理程序连续运行多次,尽管您禁用了指针事件。
  • 这不适用于以编程方式单击的按钮。
  • 这非常适合临时用例,因为要重新启用点击,您首先不需要知道点击的内容。只需将其设置回自动即可。
【解决方案2】:

这是人工状态变量解决方案的更惯用的替代方案:

$("#button_id").one('click', DoSomething);

function DoSomething() {
  // do something.

  $("#button_id").one('click', DoSomething);
}

一个只会执行一次(直到再次附加)。更多信息在这里:http://docs.jquery.com/Events/one

【讨论】:

  • -1;这不起作用。例如,尝试在此小提琴中多次快速单击按钮:jsfiddle.net/at75H/1
  • @MarkAmery:这不是因为您在回调中重新附加了处理程序吗?如果你不这样做,它会按预期工作:jsfiddle.net/at75H/2
  • 您在答案中的回调中重新附加处理程序,这是我的小提琴基础。您需要在 some 点重新附加它,否则您将永久禁用处理程序而不是暂时这样做。我认为这里干净的解决方案是使用 0ms setTimeout 调用来推迟处理程序的重新附加 - 如果有机会,我会写一个关于这个的答案。
【解决方案3】:
$("#button_id").click(function() {
  if($(this).data('dont')==1) return;
  $(this).data('dont',1);
  //do something
  $(this).data('dont',0);
}

请记住,$.data() 仅适用于具有 ID 的项目。

【讨论】:

  • 我比 .one 选项更喜欢这个解决方案。我一直在处理可能会观察到一个事件的元素。通过使用 .one 或再次调用 .off 和 .on,我将更改回调的顺序。通过添加回调函数观察到的数据属性,您可以保持回调顺序不变
  • 您提前返回这里将绝对不会生效。 JavaScript 是单线程的,因此如果对该函数的多次调用排队,您可以保证在第二次调用开始时,第一次调用已经完成并将您的 dont 属性设置回 0。
【解决方案4】:

您可以使用.off 取消绑定您的处理程序,但有一个警告;如果你这样做只是为了防止处理程序在它已经运行时再次被触发,你需要推迟重新绑定处理程序。

例如,考虑这段代码,它使用 5 秒的热睡眠来模拟在处理程序内完成的同步和计算成本高昂的事情(比如繁重的 DOM 操作):

<button id="foo">Click Me!</div>
<script>
    function waitForFiveSeconds() {
        var startTime = new Date();
        while (new Date() - startTime < 5000) {}
    }
    $('#foo').click(function handler() {
        // BAD CODE, DON'T COPY AND PASTE ME!
        $('#foo').off('click');
        console.log('Hello, World!');
        waitForFiveSeconds();
        $('#foo').click(handler);
    });
</script>

这行不通。正如您在this JSFiddle 中尝试的那样,如果您在处理程序已经执行时单击按钮,则处理程序将在第一次执行完成后再次执行。更重要的是,至少在 Chrome 和 Firefox 中,即使您不使用 jQuery 而是使用 addEventListenerremoveEventListener 添加和删除处理程序也是如此。浏览器在第一次单击后执行处理程序,解除绑定和重新绑定处理程序,然后处理第二次单击并检查是否有单击处理程序要执行。

要解决这个问题,您需要使用setTimeout 推迟处理程序的重新绑定,以便在第一个处理程序执行时发生的点击将在您重新附加处理程序之前处理。 p>

<button id="foo">Click Me!</div>
<script>
    function waitForFiveSeconds() {
        var startTime = new Date();
        while (new Date() - startTime < 5000) {}
    }
    $('#foo').click(function handler() {
        $('#foo').off('click');
        console.log('Hello, World!');
        waitForFiveSeconds();

        // Defer rebinding the handler, so that any clicks that happened while
        // it was unbound get processed first.
        setTimeout(function () {
            $('#foo').click(handler);
        }, 0);
    });
</script>

您可以在 this modified JSFiddle 看到这一点。

当然,如果您在处理程序中执行的操作已经是异步的,则这是不必要的,因为您已经将控制权交给了浏览器,并在重新绑定处理程序之前让它刷新所有点击事件。例如,这样的代码在没有setTimeout 调用的情况下也能正常工作:

<button id="foo">Save Stuff</div>
<script>
    $('#foo').click(function handler() {
        $('#foo').off('click');
        $.post( "/some_api/save_stuff", function() {
            $('#foo').click(handler);
        });
    });
</script>

【讨论】:

  • 谢谢你。过去几天我遇到了这个确切的问题,我需要暂时取消绑定我们正在使用的焦点事件。
  • 谢谢!我有一个稍微不同的用例,但使用 setTimeout 允许函数在单击处理程序重新连接之前退出。经过数小时的故障排除后,这救了我。
【解决方案5】:

你可以像我之前告诉你的其他人一样使用外观:

A.) 使用按钮元素的 .data 来共享外观变量(或只是全局变量)

if ($('#buttonId').data('locked') == 1)
    return
$('#buttonId').data('locked') = 1;
// Do your thing
$('#buttonId').data('locked') = 0;

B.) 禁用鼠标信号

$("#buttonId").css("pointer-events", "none");
// Do your thing
$("#buttonId").css("pointer-events", "auto");

C.) 如果是 HTML 按钮,您可以禁用它(输入 [type=submit] 或按钮)

$("#buttonId").attr("disabled", "true");
// Do your thing
$("#buttonId").attr("disabled", "false");

但要注意其他线程!我失败了很多次,因为我的动画(淡入或淡出)花了一秒钟。 例如。 fadeIn/fadeOut 支持回调函数作为第二个参数。 如果没有其他方法,请使用setTimeout(callback, delay)

您好, 托马斯

【讨论】:

  • 在浏览器中执行的JavaScript是单线程的;我不知道您所说的“注意其他线程”是什么意思,但我很确定您误解了正在发生的事情。
  • 指针事件是链接的方式,但兼容性只适用于 IE 11。
【解决方案6】:

如果#button_id 暗示一个标准的 HTML 按钮(如提交按钮),您可以使用 'disabled' 属性使按钮对浏览器无效。

$("#button_id").click(function() {
    $('#button_id').attr('disabled', 'true');

    //do something

     $('#button_id').removeAttr('disabled');
});   

但是,您可能需要注意的是这些事情发生的顺序。如果您使用 jquery hide 命令,您可能需要包含“$('#button_id').removeAttr('disabled');”作为回调的一部分,以便在隐藏完成之前不会发生。

[edit] 使用回调的函数示例:

$("#button_id").click(function() {
    $('#button_id').attr('disabled', 'true');
    $('#myDiv').hide(function() { $('#button_id').removeAttr('disabled'); });
});   

【讨论】:

  • 像这里的大多数答案一样,这个答案(或至少第一个代码示例)是有缺陷的:您的第一个示例 同步 禁用按钮,执行处理程序,然后重新启用按钮,所有之前浏览器考虑如何处理下一次点击。请参阅jsfiddle.net/at75H/18 以获取此失败的演示;如果您打开控制台并多次单击该按钮,您将看到处理程序多次触发,尽管添加了 disabled 属性。您需要推迟删除该属性(例如使用setTimeout)才能使其正常工作。
【解决方案7】:

尝试使用.one()

var button = $("#button"),
  result = $("#result"),
  buttonHandler = function buttonHandler(e) {
    result.html("processing...");
    $(this).fadeOut(1000, function() {
      // do stuff
      setTimeout(function() {
        // reset `click` event at `button`
        button.fadeIn({
          duration: 500,
          start: function() {
            result.html("done at " + $.now());
          }
        }).one("click", buttonHandler);

      }, 5000)
    })
  };

button.one("click", buttonHandler);
#button {
  width: 50px;
  height: 50px;
  background: olive;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div id="result"></div>
<div id="button">click</div>

【讨论】:

  • 差点给这个-1,但也许我太苛刻了。与大多数答案不同,这有效(因为您推迟使用setTimeout 阅读处理程序),但没有解释发生了什么;如果我还没有理解延迟读取处理程序的必要性,我觉得这对我没有用。
  • @MarkAmery 对此几乎投了-1,但也许我太苛刻了。与大多数答案不同,这有效(因为您使用setTimeout 推迟读取处理程序),但没有解释发生了什么 实际方法是使用.one()setTimeout.fadeOut().fadeIn() 用于演示使用.one()。即事件最多被调用一次;当所有同步或异步进程完成时,使用.one() 重新附加事件。如果在任务完成之前点击button,则不会附加任何事件;事件已附加,一次重新附加一个
【解决方案8】:

最好使用当前事件并且不要在全局处理程序中保存处理程序。 我得到当前元素事件然后解除绑定然后再次绑定。 对于处理程序。

var element =  $("#elemid")[0];
var tempHandler = jQuery._data(element)["events"]["click"][0].handler;
$("#elemid").unbind("click");

// do the job that click not suppose to listen;
$("#elemid").bind("click" , tempHandler );

所有处理程序

var element =  $("#elemid")[0];
var clickHandlerList = jQuery._data(element)["events"]["click"];
var handlerList = [];
for(var i  = 0 ; i <  clickHandlerList .length ; i++) {
    handlerList .push(clickHandlerList [i].handler);
}
$("#elemid").unbind("click");
// do the job that click not suppose to listen;
for(var i  = 0 ; i <  handlerList.length ; i++) {
    // return back all handler to element.
    $("#elemid").bind("click" , handlerList[i]);
}

【讨论】:

    【解决方案9】:

    我在等待函数完成时尝试显示加载微调器时几乎没有遇到这个问题。因为我将微调器附加到 HTML 中,所以每次单击按钮时都会复制微调器,如果您不反对在全局范围内定义变量,那么这对我来说效果很好。

        var hasCardButtonBeenClicked = '';
        $(".js-mela-card-button").on("click", function(){  
            if(!hasCardButtonBeenClicked){
                hasCardButtonBeenClicked = true;
                $(this).append('<i class="fa fa-circle-o-notch fa-spin" style="margin-left: 3px; font-size: 15px;" aria-hidden="true"></i>');
            }    
        });
    

    注意,我所做的只是声明一个变量,只要它的值为null,点击之后的动作就会发生,然后将变量值设置为“true”(它可以是任何值,只要它不为空),进一步禁用按钮,直到刷新浏览器或变量设置为 null。

    回想起来,将 hasCardButtonBeenClicked 变量设置为“false”开始可能更有意义,然后根据需要在“true”和“false”之间交替。

    【讨论】:

      【解决方案10】:

      这个例子有效。


      HTML 代码:

        <div class="wrapper">
           <div class="mask">Something</div> 
        </div>
      

      jQuery:

          var fade = function(){
              $(".mask").fadeToggle(500,function(){
                  $(this).parent().on("click",function(){
                      $(this).off("click");
                      fade();
                  });
              });
          };
      
          $(".wrapper").on("click",function(){
              $(this).off("click");
              fade();     
          });
      

      【讨论】:

        【解决方案11】:
        $("#button_id").click(function() {
            $('#button_id').attr('disabled', 'true');
            $('#myDiv').hide(function() { $('#button_id').removeAttr('disabled'); });
        }); 
        

        不要用.attr()做残疾人,用.prop(),这样更好。

        【讨论】:

          【解决方案12】:

          此代码将在按钮标签上显示加载,并将按钮设置为禁用状态,然后处理后重新启用并返回原始按钮文本:

          $(function () {
          
                  $(".btn-Loading").each(function (idx, elm) {
                      $(elm).click(function () {
                          //do processing
                          if ($(".input-validation-error").length > 0)
                              return;
                          $(this).attr("label", $(this).text()).text("loading ....");
                          $(this).delay(1000).animate({ disabled: true }, 1000, function () {
                              //original event call
                              $.when($(elm).delay(1000).one("click")).done(function () {
                                  $(this).animate({ disabled: false }, 1000, function () {
                                      $(this).text($(this).attr("label"));
                                  })
                              });
                              //processing finalized
                          });
                      });
                  });
                  // and fire it after definition
              }
             );
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多