Backbone.js View can't unbind events properly

2020-02-17 10:34发布

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?

5条回答
唯我独甜
2楼-- · 2020-02-17 10:52

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.

查看更多
淡お忘
3楼-- · 2020-02-17 10:58

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.

查看更多
干净又极端
4楼-- · 2020-02-17 11:00

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

this.undelegateEvents();
查看更多
走好不送
5楼-- · 2020-02-17 11:00

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(){
    ........
},
查看更多
冷血范
6楼-- · 2020-02-17 11:11

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 i.e. 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

查看更多
登录 后发表回答