Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone.js View can't unbind events properly

I have some Backbone.js code that bind a click event to a button, and I want to unbind it after clicked, the code sample as below:

var AppView = Backbone.View.extend({
    el:$("#app-view"),
    initialize:function(){
        _.bindAll(this,"cancel");
    },

    events:{
        "click .button":"cancel"
    },

    cancel:function(){
        console.log("do something...");
        this.$(".button").unbind("click");
    }
});
var view = new AppView();

However the unbind is not working, I tried several different way and end up binding event in initialize function with jQuery but not in Backbone.events model.

Anyone know why the unbind is not working?

like image 841
Jimchao Avatar asked Jul 26 '11 14:07

Jimchao


3 Answers

The reason it doesn't work is that Backbonejs doesn't bind the event on the DOM Element .button itself. It delegates the event like this:

$(this.el).delegate('.button', 'click', yourCallback);

(docs: http://api.jquery.com/delegate)

You have to undelegate the event like this:

$(this.el).undelegate('.button', 'click');

(docs: http://api.jquery.com/undelegate)

So your code should look like:

var AppView = Backbone.View.extend({
    el:$("#app-view"),
    initialize:function(){
        _.bindAll(this,"cancel");
    },

    events:{
        "click .button":"cancel"
    },

    cancel:function(){
        console.log("do something...");
        $(this.el).undelegate('.button', 'click');
    }
});
var view = new AppView();

Another (maybe better) way to solve this is to create a state attribute like this.isCancelable now everytime the cancel function is called you check if this.isCancelable is set to true, if yes you proceed your action and set this.isCancelable to false.

Another button could reactivate the cancel button by setting this.isCancelable to true without binding/unbinding the click event.

like image 128
sled Avatar answered Nov 08 '22 14:11

sled


You could solve this another way

var AppView = Backbone.View.extend({
    el:$("#app-view"),
    initialize:function(){
        _.bindAll(this,"cancel");
    },

    events:{
        "click .button":"do"
    },

    do:_.once(function(){
        console.log("do something...");
    })
});
var view = new AppView();

underscore.js once function ensures that the wrapped function can only be called once.

like image 17
bradgonesurfing Avatar answered Nov 08 '22 13:11

bradgonesurfing


There is an even easier way, assuming you want to undelegate all events:

this.undelegateEvents();
like image 7
Garrett Avatar answered Nov 08 '22 12:11

Garrett