jQuery的One - 一次绑定多个事件类型并只触发一次

11

有没有一种方法可以在任何事件被触发时只执行一次函数?

例如,如果我有以下函数:(在jsfiddle上的演示)

$('input').one('mouseup keyup', function(e){ 
    console.log(e.type);
});

无论哪个事件触发了它,我希望只调用该函数一次。

但根据 .one() 的文档:

如果第一个参数包含多个空格分隔的事件类型,则对于每种事件类型都会调用事件处理程序一次。

因此,当前该函数将针对每种事件类型触发一次。

5个回答

19

不要使用.one,而是使用.on并手动使用.off来取消绑定。

$('input').on('mouseup keyup', function(e){
    console.log(e.type);
    $(this).off('mouseup keyup');
});

Fiddle: http://jsfiddle.net/23H7J/3/


使用命名空间可以更加优雅地完成这项工作:

$('input').on('mouseup.foo keyup.foo', function(e){
    console.log(e.type);
    $(this).off('.foo');
});

这使我们能够使用单个标识符(foo)来删除任意数量的绑定,并且不会影响元素可能拥有的任何其他mouseup或keyup绑定。

示例:http://jsfiddle.net/23H7J/41/


我的思维和培根已经被拯救了!非常感谢。使用 .off 处理多个转换事件时,由于每个事件都会触发,它让我感到困惑(自己注意 - 完整阅读文档)。 - Submachine23
JQuery 命名空间不是必需的:$('input').on('mouseup keyup', function foo(e) { $(this).off('mouseup keyup', foo); }); - Dan

8

很棒的答案!为了将它们封装成一个函数,这里有一个基于当前答案的jQuery扩展:

//The handler is executed at most once per element for all event types.
$.fn.once = function (events, callback) {
    return this.each(function () {
        $(this).on(events, myCallback);
        function myCallback(e) {
            $(this).off(events, myCallback);
            callback.call(this, e);
        }
    });
};

然后这样调用:
$('input').once('mouseup keyup', function(e){ 
    console.log(e.type);
});

在fiddle中的演示

额外福利:通过将原始处理程序传递给off函数,可以使此特定函数的处理程序仅分离。否则,您需要自定义命名空间。

另外,如果您希望处理程序仅在任何元素触发任何事件并立即分离时仅触发一次,则只需从扩展中删除each,如下所示:

//The handler is executed at most once for all elements for all event types.
$.fn.only = function (events, callback) {
    var $this = $(this).on(events, myCallback);
    function myCallback(e) {
        $this.off(events, myCallback);
        callback.call(this, e);
    }
    return this
};

你能否修改你的函数,使其能够使用你在答案中展示的语法以及 $('#someParent').once('keyup etc', '.someChildrenOfParent'), function(e){ doStuff(); }); 这种语法来工作? - Drew
@Drew,你是指使用委托吗?也许你想先试一试? - KyleMit
这是我的代码,但我不确定在哪里加上“scope”,结果函数的行为类似于jQuery的.one()。https://jsfiddle.net/wyeq0h3g/ - Drew
嗯,我不确定原始解决方案是否有效,因为在此测试fiddle中,我期望橙色背景会在每次按键时切换开/关: https://jsfiddle.net/L5uzkwv2/ - Drew

4
为一次性开火并持续开火,可以使用以下代码片段: https://jsfiddle.net/wo0r785t/1/
$.fn.onSingle = function(events, callback){
    if ("function" == typeof(callback)) {
        var t = this;
        var internalCallback = function(event){
            $(t).off(events, internalCallback);
            callback.apply(t, [event]);
            setTimeout(function(){
                $(t).on(events, internalCallback);
            }, 0);
        };
        $(t).on(events, internalCallback);
    }
    return $(this);
};

它的工作原理是通过使用internalCallback函数暂时禁用事件,并异步(setTimeout)再次启用。

这个线程中的大多数其他建议会在目标上永久禁用所有未来事件。然而,虽然OP似乎对之前的答案满意,但他的问题并未提到永久禁用事件。


2
您可以在函数末尾添加$('input').off();

-1
一个更简单的方法是,可以在第一次触发后继续检测并执行函数,为此,我们需要暂时存储之前的值,并进行快速比较:如果没有改变,则不运行函数的其余部分。 jQUERY
$('input').on('keyup paste input change', function(e){ 
    var oldValue = $(e.target).data('oldvalue');
    var newValue = $(e.target).val();

    if (oldValue === newValue) //nothing has changed
        return;

    $(e.target).data('oldvalue',newValue); //store the newly updated value

    //proceed with the rest of the function
});

请注意,我正在使用.on来附加事件,而不是.onePURE JAVASCRIPT 对于纯JavaScript解决方案,需要单独添加侦听器,因为无法将多个事件传递给.addEventListener
var selector = document.querySelector('input')
selector.addEventListener('keyup', updateElement);
selector.addEventListener('paste', updateElement);
selector.addEventListener('input', updateElement);
selector.addEventListener('change', updateElement);

function updateElement(e){
    var oldValue = e.target.getAttribute('oldvalue');
    var newValue = e.target.value;

    if (oldValue === newValue) //nothing has changed
       return; 
    console.log (newValue);
    e.target.setAttribute('oldvalue', newValue); //store the newly updated value

    //proceed with the rest of the function
}

假设您想监听输入框中的更改,我建议不要使用mouseup来检测可能的右键单击>粘贴,因为鼠标可能在输入框之外释放。为了安全起见,最好使用keyuppasteinputchange,就像我在上面的代码中所做的那样。

嘿Juan,谢谢分享。我认为这并没有真正回答在这里提出的问题。如果我有一个mouseupkeyup事件,并且我想只处理一次,无论哪个被触发,比较oldValue === newValue并不能解决问题。因此,值得撤下答案并发布自己的问题,以阐明您要解决的问题。 - KyleMit

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