jQuery - 如何在事件被触发后临时禁用onclick事件监听器?

40

在事件触发后,我如何暂时禁用onclick事件监听器(优先使用jQuery)?

例如:

用户单击按钮并触发下面的函数后,我希望禁用onclick监听器,以便不向我的django视图发送相同的命令。

$(".btnRemove").click(function(){
   $(this).attr("src", "/url/to/ajax-loader.gif");
   $.ajax({
        type: "GET",
        url: "/url/to/django/view/to/remove/item/" + this.id,
        dataType: "json",
        success: function(returned_data){
            $.each(returned_data, function(i, item){
              // do stuff                       
     });
   }
});

非常感谢,

Aldo


暂时如何?在您希望再次接受点击事件之前,需要发生什么事情? - Ty W
9
(请注意,在翻译过程中,我会尽力保持原文意思的准确性和流畅性。)不允许使用 GET 请求来执行状态更改。 - bobince
2
@bobince:哦,如果我们都遵守这个简单的规则,那不是太好了吗... @OP:关于Bob所说的内容(以令人惊恐的晦涩文字形式)可以在这里找到更多信息:http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2 - T.J. Crowder
也许我没有解释清楚:我有一个购物车中的物品列表。每个物品都有一个图像,其id是物品id,并且具有class =“btnRemove”(我知道它不是按钮)。当用户单击删除按钮时,我将按钮更改为预加载器图像。我想要的是:当图像变为加载时,它不再接受点击,因此不会触发相同的命令。@bobince:你是对的,但我认为这是一种很酷的方法,可以在不重新加载页面的情况下删除项目。 - aldux
10个回答

66

有很多方法可以实现。例如:

$(".btnRemove").click(function() {
    var $this = $(this);
    if ($this.data("executing")) return;
    $this
        .data("executing", true)
        .attr("src", "/url/to/ajax-loader.gif");
    $.get("/url/to/django/view/to/remove/item/" + this.id, function(returnedData) {
        // ... do your stuff ...
        $this.removeData("executing");
    });
});
或者
$(".btnRemove").click(handler);

function handler() {
    var $this = $(this)
        .off("click", handler)
        .attr("src", "/url/to/ajax-loader.gif");
    $.get("/url/to/django/view/to/remove/item/" + this.id, function(returnedData) {
        // ... do your stuff ...
        $this.click(handler);
    });
}
我们也可以使用事件委托来编写更清晰的代码并提高性能:
$(document).on("click", ".btnRemove:not(.unclickable)", function() {
    var $this = $(this)
        .addClass("unclickable")
        .attr("src", "/url/to/ajax-loader.gif");
    $.get("/url/to/django/view/to/remove/item/" + this.id, function(returnedData) {
        // ... do your stuff ...
        $this.removeClass("unclickable");
    });
});

如果我们不需要在处理程序被执行后重新启用它,那么我们可以使用.one()方法。它绑定只执行一次的处理程序。请参阅jQuery文档:http://api.jquery.com/one


3
在这种情况下,您应该使用 .one() 方法将处理程序绑定到仅执行一次的事件。请参阅jQuery文档:http://docs.jquery.com/Events/one - thorn0
3
如果您更喜欢设置disabled属性而不是使用类,.on("click", ".btmRemove:not([disabled])"是另一个选项。 - AaronLS
disabled 属性仅适用于按钮输入元素,而不适用于 <a> 元素。 - timaschew
1
@timaschew 是的,但问题不是关于禁用<a>元素,而是禁用点击处理程序。在这种情况下,<a>上的disabled属性充当一个标志,以排除我们的点击处理程序。 - AaronLS
这根本没有回答问题。他不是在寻找基于某些标志从现有事件处理程序返回的hacky方式,也不是分离和重新附加处理程序...他想知道如何暂时禁用所有事件。他想保持事件处理程序附加而不改变它,但希望能够“禁用”元素,使其点击不会生成事件。“pointer-events: none;” CSS属性可能是最接近的东西,但它没有解决键盘事件。当然,这一切在十年前被Flash解决了。回到过去的感觉真好。 - Triynko
显示剩余3条评论

19

你想要禁用点击事件监听器的时间有多长?一种方法是使用jQuery的unbind http://docs.jquery.com/Events/unbind取消绑定事件监听器。

但最好的做法是不要仅仅为了稍后重新绑定而解除绑定事件监听器。而是使用一个布尔值来代替。

var active = true;
$(".btnRemove").click(function() {
    if (!active) {
        return;
    }
    active = false;
    $(this).attr("src", "/url/to/ajax-loader.gif");
    $.ajax({
        type: "GET",
        url: "/url/to/django/view/to/remove/item/" + this.id,
        dataType: "json",
        success: function(returned_data) {
            active = true; // activate it again !
            $.each(returned_data, function(i, item) {
                // do stuff                       
            });
        }
    });
});

编辑: 为了安全起见,您还应该关注其他ajax完成例程(仅有三个:successerrorcomplete 参见文档),否则active可能会保持不变。


这个是如何工作的,active只是一个在开头被读取的布尔值。 - TStamper
2
active 保持为 false 直到 ajax 请求成功完成。在此期间,点击处理程序立即在开头返回,因此基本上不会执行。 - RamboNo5
我喜欢返回false的简单性...我不知道它是否比某种方式停止传播更好的做法,但在我的情况下它更容易且有效 =) - Edward
这不管输入什么,都会返回吗? - Taylor A. Leach
@TaylorA.Leach 既然你提到了,我认为 if 语句中的第一个检查应该是 if (!active)。我会更新答案。 - RamboNo5

5
为什么不禁用这个按钮?你有特定的原因想要禁用这个监听器吗? 另外,从你的代码中我看到你正在进行一个ajax调用。所以你特别想阻止用户直到调用返回吗?如果是的话,你可以尝试使用blockUI,这是一个jQuery插件。

3
我会设置一个全局变量来跟踪AJAX请求...
var myApp = {
  ajax: null
}

然后,有这么一点魔法可以阻止同时请求...

// Fired when an AJAX request begins
$.ajaxStart(function() { myApp.ajax = 1 });

// Fired when an AJAX request completes
$.ajaxComplete(function() { myApp.ajax = null });

// Fired before an AJAX request begins
$.ajaxSend(function(e, xhr, opt) {
  if(myApp.ajax != null) {
    alert("A request is currently processing. Please wait.");
    xhr.abort();
  }
});

通过这种方法,您不需要回到代码中并修改每个AJAX调用。 (我称之为“附加”解决方案)


从jQuery 1.9开始,所有的处理异步请求的全局函数,包括使用.ajaxStart()添加的函数,都必须附加到 document 对象上。 - ooXei1sh

2

您可以根据布尔值在单击内执行操作。当单击时,更改布尔值并使用setTimeout()在几秒钟后将其更改回来。这将有效地限制用户每隔几秒钟只能单击一次按钮。

var isEnabled = true;

$("a.clickMe").click(function(e){
  e.preventDefault();
  if (isEnabled == true) {
    isEnabled = false; // disable future clicks for now
    do_Something();
    setTimeout(function(){
      isEnabled = true;
    }, 3000); // restore functionality after 3 seconds
  }
});

2

我会使用一个类,例如'ajax-running'。只有在被点击的元素没有'ajax-running'类时,才会执行点击事件。当你的ajax调用完成后,你可以删除'ajax-running'类,这样它就可以再次被点击。

$(".btnRemove").click(function(){
    var $button         = $(this);
    var is_ajaxRunning  = $button.hasClass('ajax-running');
    if( !is_ajaxRunning ){
        $.ajax({
            ...
            success: function(returned_data) {
                ...
                $button.removeClass('ajax-running');
            });
        };
    }   
});

1
var ajaxAction = function() {
    var ele = $(this);
    ele.unbind("click", ajaxAction);
    ele.attr("src", "/url/to/ajax-loader.gif");
    $.ajax({
        type: "GET",
        url: "/url/to/django/view/to/remove/item/" + this.id,
        dataType: "json",
        success: function(returned_data) {
            $.each(returned_data, function(i, item) {
            });
        },
        complete: function() {
            ele.bind("click", ajaxAction);
        }
    });
}
$(".btnRemove").click(ajaxAction);

我其实正要添加这个。将其命名并在内部解绑/重新绑定比这些示例中跳过所有其他步骤更加简洁。 - Erik Reppen

1

我建议在Ajax完成例程(成功或失败,记住)中禁用按钮,然后重新启用它。如果您担心浏览器不尊重您禁用按钮,您可以在按钮上备份自己的标志(例如,使用data-前缀设置名为data-disabled的属性,以符合良好实践并与HTML5兼容)。但是,除非实际遇到浏览器未禁用按钮的问题,否则我可能认为这已经足够好了。


1
如果您正在通过ajax发布,请在单击时禁用按钮,并在过程完成后启用它。但是,如果您正在回传,则不能仅禁用按钮。禁用按钮会导致服务器端单击事件无法触发。因此,只需在单击时隐藏按钮并显示用户友好消息即可。如何在postback上禁用asp.net按钮的文章可以帮助您。

0

你也可以隐藏按钮(或包含的 div 元素)

$(".btnRemove").click(function() {
   $(this).hide();
   // your code here...
})

如果需要再次显示,您可以直接调用.show()。

另外,根据您的用例,您应该考虑使用加载覆盖。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接