jQuery off()在使用bind时无法解绑事件

11
function bubble(content, triggerElm){
  this.element = $('<div class="bubble" />').html(content);
  this.element.css(.....) // here is positioned based on triggerElm
}

bubble.prototype.show = function(){
  $(document).on('click', this._click.bind(this));
  this.element.css(....)
};

bubble.prototype.hide = function(){
  $(document).off('click', this._click.bind(this));
  this.element.css(....)
};  

bubble.prototype._click = function(event){
  console.log('click', this);

  if(this.element.is(event.target) || (this.element.has(event.target).length > 0))
    return true;

  this.hide();
};

var b = new bubble();
b.show();
b.hide();

我一直在控制台中看到点击事件(click),所以点击事件没有解绑。 但是如果我删除 bind() 调用,那么点击事件就会解绑。有谁知道为什么吗?我需要一种方法来能够在我的测试函数内更改“this”,这就是为什么我在使用 bind()

6个回答

6
问题在于this._click.bind()每次被调用时都会创建一个新的函数。为了分离特定的事件处理程序,您需要传递用于创建事件处理程序的原始函数,但这里没有发生,因此处理程序没有被删除。
如果您的应用程序中只有几个,您可以简单地不使用this。这将消除关于this指的是什么的混淆,并确保每个保留对其自己的click函数的引用,以便根据需要删除事件:
function bubble(content, triggerElm) {
    var element = $('<div class="bubble" />').html(content);
    element.css(.....); // here is positioned based on triggerElm

    function click(event) {
        console.log('click', element);
        if (element.is(event.target) || 
            element.has(event.target).length > 0) {
            return true;
        }
        hide();
    }

    function show() {
        $(document).on('click', click);
        element.css(....);
    }

    function hide() {
        $(document).off('click', click);
        element.css(....);
    } 

    return {
        show: show,
        hide: hide
    };
}

var b1 = bubble(..., ...);
b1.show();

var b2 = bubble(..., ...);
b2.show();

看看这如何使您不必使用像.bind()和前缀为下划线的方法这样的人为手段。


好的,我需要能够判断点击是否在气泡元素之外,这是实例的一部分。基本上,在点击函数内部,我正在检查e.target是否为this.element或其内部。 - Elfy
@Elfy 我在你的示例中没有看到任何this.element。你能展示一下它是在哪里引入的,以及click()函数如何使用它吗?我相当确定我的示例可以适应它。 - JLRishe
它在构造函数中。我认为不可能将其设置为全局,因为页面上应该可以同时显示多个气泡。 - Elfy
@Elfy 我的代码中没有全局变量,也没有任何阻止您同时使用多个气泡的东西。您能展示一下 this.element 如何被使用吗? - JLRishe
有趣的 :) 我以前没有见过这样的代码。但是我需要更深入地研究一下,看看与原型的比较有哪些缺点。 - Elfy
显示剩余3条评论

5

Josh Crozier,这意味着与该命名空间相关的每个事件都将被清除。不确定他是否想要这样。 - Neta Meta
1
@NetaMeta 所以只需给它一个独特的名称,就不会有问题了。 - Josh Crozier
1
@ Josh Crozier - 那么为什么要使用闭包或者为什么要保持全局变量的清晰 - 只需要给你的变量命名独特的名称即可... - Neta Meta

4

尝试使用 jQuery 的代理来获得您的函数的唯一引用。

这样,当您调用 $.proxy(test, this) 时,它将检查此函数是否已被引用。如果是,则代理会返回该引用,否则会创建一个并返回给您。因此,您可以始终获得原始函数,而不必反复创建它(如使用 bind 一样)。

因此,当您调用 off() 并传递测试函数的引用时,off() 将从单击事件中删除您的函数。

而且,在使用之前应该先声明您的测试函数。

var test = function(){
      console.log('click');
};    

$(document).on('click', $.proxy(test, this));
$(document).off('click', $.proxy(test, this));

http://jsfiddle.net/aw50yj7f/


1
针对您的情况,我建议使用Josh Crozier的解决方案,这是最好的一个。 - Neta Meta
命名空间的问题在于我必须保持某种计数器变量。 - Elfy

3
请阅读https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind bind创建一个新函数,因此执行$(document).on('click', test.bind(this));就像执行$(document).on('click', function(){});一样,每次执行都会调用一个新的匿名函数,因此您没有引用可解绑。
如果您这样做:
var test = function(){
     console.log('click');
};

var newFunct = test.bind(this);
$(document).on('click', newFunct );
$(document).off('click', newFunct );

它应该可以正常工作。

例如:http://jsfiddle.net/508dr0hv/

另外,使用 bind 不是推荐的做法,它会减慢速度并且在一些浏览器中不被支持。


谢谢。但是如果我的测试函数是对象的一部分,那该如何绑定它呢?我的意思是在哪里进行绑定? :) - Elfy
1
Elfy,请在你的问题中添加这个案例以展示你的意思。 - Neta Meta
1
针对您的更新,我建议使用Josh Crozier的解决方案,或者将show/hide指向另一个带有参数指示显示或隐藏的函数,并在那里进行绑定,或者在类外部进行整个绑定。 - Neta Meta

0
如果在使用类时遇到了这个问题,我找到了一个替代方法,可以将处理程序的定义替换为绑定函数的赋值。
//Replace this
onclick(e) {
    //...
}

//With this:
onclick = function(e) {
    //...
}.bind(this);

这个不是很漂亮,但如果你已经写好了代码,它可以避免重构。

0
不要将其绑定到事件上,而是将其作为参数发送。
$("#DOM").on("click",{
'_this':this
},myFun);

myFun(e){
 console.info(e.data._this);
$("#DOM").off("click",myFun);
}

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