Backbone DOM events firing multiple times

backbone-events, backbone.js, javascript, jquery

Solution

Since you omitted the selector in your `events` object of your views the following applies

Per the Backbone Documentation: `Omitting the selector causes the event to be bound to the view's root element (this.el).`

The problem is each of the `SeriesGridItemView`'s bind `click` events to `#model-grid` and each view is a child of `#model-grid`. In your example, you register 5 click events and when you click on any of your views, all 5 events are triggered.

Without changing any of your other code, one solution is to setup you `events` object to return a function so you can specify an `id` selector for each of your views.

events: function() {
  var selector = '#series-' + this.model.get('seriesId'); // this id corresponds to your template id
  var ev = {};
  ev['click ' + selector] = 'toggle';
  return ev;
},

Another option and the one I prefer, is to not specify `#model-grid` as your root element for all your views. It would end up looking like: demo

SeriesGridItemView = Backbone.View.extend({
  // remove the following line
  el:'#model-grid', 

  // .. all else is the same ../
});

series = new SeriesCollection(window.BMW.data.series);
  series.forEach(function(m,i){
    var c = i+1;
    if(c > 3){
      c%=3;
    }
    m.set('column','1');
    $('#model-grid').append(new SeriesGridItemView({model:m}).el);
});

A side suggetion

In your `render` function, there's no need to create variable, you can access your element by using `$`:

render: function(){
  this.template = _.template( $('#template-model-grid-item-view').html() );
  this.$el.append(this.template(this.model.toJSON())));
},
setState: function(){
  this.active = this.model.get('active');
  this.$el.toggleClass('active',this.active);
},

Problem

I am building a backbone app for the first time - and it is going great. However, I don't think I am creating views for my collection of models in the correct way and when I bind events they fire for every view when I only want them to fire for one. Here is my backbone code (a snippet): ``` (function(){ Series = Backbone.Model.extend({ defaults:{ active:false } }); SeriesGridItemView = Backbone.View.extend({ el:'#model-grid', defaults: { active : false }, initialize: function(){ this.render(); this.listenTo(this.model, 'change', this.setState); }, render: function(){ this.template = _.template( $('#template-model-grid-item-view').html() ); this.view = $(this.template(this.model.toJSON())).appendTo(this.$el); }, setState: function(){ this.active = this.model.get('active'); this.view.toggleClass('active',this.active); }, events: { 'click':'toggle' }, toggle: function(e){ e.stopPropagation(); e.preventDefault(); console.log('clicked'); return false; } }); SeriesCollection = Backbone.Collection.extend({ model: Series, setPrice : function(p){ this.forEach(function(m){ var active = 0; _.each(m.get('vehicles'),function(v){ if(v.price <=p){ v.active = true; active++; } else{ v.active = false; } }); m.set('active',active>0); }); } }); series = new SeriesCollection(window.BMW.data.series); series.forEach(function(m,i){ var c = i+1; if(c > 3){ c%=3; } m.set('column','1'); new SeriesGridItemView({model:m}); }); })(); ``` And here is the JSON that constructs the models: ``` window.BMW.data.series = [ { seriesId:1, name:'1 Series', slug:'1-series', order:0, vehicles:[ { seriesId:1, price:200 }, { seriesId:2, price:300 } ] }, { seriesId:2, name:'3 Series', slug:'3-series', order:1, vehicles:[ { seriesId:1, price:400 }, { seriesId:2, price:500 } ] }, { seriesId:3, name:'4 Series', slug:'4-series', order:3, vehicles:[ { seriesId:1, price:100 }, { seriesId:2, price:300 } ] }, { seriesId:4, name:'6 Series', slug:'6-series', order:4, vehicles:[ { seriesId:1, price:100 }, { seriesId:2, price:300 } ] }, { seriesId:6, name:'X3', slug:'x3', order:5, vehicles:[ { seriesId:1, price:500 }, { seriesId:2, price:800 } ] } ]; ``` And here is my template for the views ``` <script type="text/template" id="template-model-grid-item-view"> <div id="series-<%=seriesId%>" class="grid-item-view column-<%=column%>"> <div class="label"><%= name %></div> <div class="thumbnail"> <img src="/Content/themes/BMW/img/series/small/<%= slug %>.png"/> </div> </div> </script> ``` The views assemble correctly, but when I click one view, the event fires on all of the views. Can someone please point me in the right direction?

Original source