57658

Backbone DOM events firing multiple times

Question:

Hi guys I'm building a backbone app for the first time - and its 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>

<strong>The problem</strong> - 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?

Thanks,

Jack

Answer1:

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

Per the Backbone <a href="http://backbonejs.org/#View-delegateEvents" rel="nofollow">Documentation</a>: 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: <a href="http://jsbin.com/upowew/6/edit" rel="nofollow">demo</a>

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

<ol><li>

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); }, </li> </ol>

Recommend

  • How to remove button text responsively in JQuery Mobile
  • Backbone: bind event happen multiple times
  • How to organize Backbone collection with a specific selection?
  • How convert a POJO into a readable string on Android for debugging?
  • Stop table row toggle upon clicking link
  • Select more checkbox by value or id
  • Angular/Ionic handling successful 200 as an error
  • How do I refer to an appended item in jQuery?
  • Invoke click events on two superimposed, non-hierarchical elements
  • Add click event on window, but ignore all links that start with https
  • jquery - how to CUT LI elements from one UL to another UL
  • Angular JS: Javascript not working inside custom directive template
  • Clockpicker: How to prevent click on input to open clockpicker
  • How to drag image clone after drop.?
  • Programmatically change first page jQuery Mobile shows
  • load a page using POST to iframe
  • AJAX jquery json sending array to php
  • How I can I delay the expansion of a collapsible until its content is fetched?
  • JITSU FAILED TO INSTALL OSX [node 0.8.17 and NPM 1.2.0] WTF
  • Bootstrap Carousel Next/Prev not working
  • jQuery panel slider opens with button click but won't close
  • JQuery Mobile 1.4 How to Disable Hover Effect on Mobile Devices
  • Onclick or href which is best for opening an link in button
  • Difficult to stop infinite CSS animation in Android browser
  • jQuery toggle hide on click elsewhere
  • Jquery Show & ScrollTop (or ScrollTo)
  • CSS: How to fix overlapping divs
  • Change navbar in bootstrap if user login
  • Android Chronometer starts and stops but carries on counting when stopped
  • Ajax Upload File: $_FILES is empty but files exists in request header
  • Make VS2015 use angular-cli ng at build time in a .NET project
  • How to clear text inside text field when radio button is select
  • Why ng-show works with ng-repeat but ng-if doesn't? [duplicate]
  • Can a Chrome extension content script make an jQuery AJAX request for an html file that is itself a
  • Upload files with Ajax and Jquery
  • jquery mobile loadPage not working
  • How to pass list parameters for each object using Spring MVC?
  • Data Validation Drop Down Box Arrow Disappearing
  • Proper way to use connect-multiparty with express.js?
  • How do you join a server to an Active Directory (domain)?