Search code examples
javascriptjquerybindjquery-events

jQuery off() is not unbinding events when using bind


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();

I keep seeing click in the console, so the click does not unbind. But if I remove the bind() call the click is unbinded. Does anyone know why? I need a way to be able to change "this" inside my test function, that's why I'm using bind().


Solution

  • The problem is that this._click.bind() creates a new function every time it's called. In order to detach a specific event handler, you need to pass in the original function that was used to create the event handler and that's not happening here, so the handler is not removed.

    If there are only going to be a few bubbles in your app, you could and simply not use this. That will remove a lot of the confusion about what this is referring to and ensure that each bubble retains a reference to its own click function that can be used to remove the event as needed:

    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();
    

    See how this frees you from using contrivances like .bind() and underscore-prefixed methods.