简体   繁体   中英

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?

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.

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.

假设您要取消所有事件的取消操作,有一种更简单的方法:

this.undelegateEvents();

I like bradgonesurfing answer. However I came across a problem using the _.once approach when multiple instances of the View are created. Namely that _.once would restrict the function to be called only once for all objects of that type ie the restriction was at the class level rather than instance level.

I handled the problem this way:

App.Views.MyListItem = Backbone.View.extend({
  events: {
    'click a.delete' : 'onDelete'
  },

  initialize: function() {
    _.bindAll(this);
    this.deleteMe = _.once(this.triggerDelete);
  },

  // can only be called once
  triggerDelete: function() {
    console.log("triggerDelete");
    // do stuff
  },

  onDelete:(function (e) {
    e.preventDefault();
    this.deleteMe();
  })
});

Hopefully this will help someone

you can simply use object.off , the code below is work for me

initialize:function () {

    _.bindAll(this, 'render', 'mouseover', 'mouseout', 'delete', 'dropout' , 'unbind_mouseover', 'bind_mouseover');
    .......

},

events: {
    'mouseover': 'mouseover',
    'unbind_mouseover': 'unbind_mouseover',
    'bind_mouseover': 'bind_mouseover',
    .....
},

mouseover: function(){
    $(this.el).addClass('hover');
    this.$('.popout').show();
},

unbind_mouseover: function(){
    console.log('unbind_mouseover');
    $(this.el).off('mouseover');
},
bind_mouseover: function(){
    ........
},

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM