Backbone.js button click event is fired for all instances of the button instead of just the one that is clicked. Why?

Vlad Nicula picture Vlad Nicula · Aug 23, 2011 · Viewed 9.7k times · Source

I am learning backbone.js and am quite new. I have a view that acts as a button:

simpleButton = Backbone.View.extend({
     template: "<button class='${classes}'>${text}</button>",

     el: $("body"),

     events: {
         "click": "onClick",
         "focus": "onFocus",
         "blur": "onBlur"
     },

     initialize: function (args) {

         _.bindAll(this, 'render');
         this.rendered = false;
         this.text = args.text || 'button';
         this.classes = args.classes || [];
         this.classes.push('ui-button');
         //console.debug("Wh.views.simpleButton.initialize classes ",this.classes);
         if (args.autoRender === true) this.render();

     },

     render: function () {
         //console.debug("Wh.views.simpleButton.render classes ",this.classes);
         if (this.rendered === false) {
             $.tmpl(
                 this.template, {
                     classes: this.classes.join(' '),
                     text: this.text
                 }
             ).appendTo(this.el);
             this.rendered = true;
         }

     },

     //event handlers
     onClick: function (ev) {
         console.debug(this);
         alert("click on ", ev, this);
     },

     onFocus: function (ev) {
         ////console.debug(ev);
     },

     onBlur: function (ev) {

     }

 });

My problem is that if I create two buttons, and click just one of them, I get the alert box two times, and the debug showing me "this" shows the first button first, and the second button next.

Am I missing something?

Answer

charlysisto picture charlysisto · Aug 23, 2011

The events you define are bound to the "el" property of your view. In your case it is "body" so when you fire up click with 2 simpleButton views instantiated, you have 2 of them listening for the same event.

Each view you instantiate should represent one and only one DOM element defined by the el property. So if you want to create a button view (not sure this is 'best practice' in a real program) you could have :

SimpleButton =  Backbone.View.extend({
        template : "<button class='${classes}'>${text}</button>",

        tagName : "div", // defines the html tag that will wrap your template
        className: ".buttonbox", 
        ...
});

mybtn = new SimpleButton();
mybtn.render().appendTo('body')

That way your click event will only concern the one div.buttonbox inside of which your button lives.

Notice : Backbone idea of the render function is creating an html string you'll use afterwards to append prepend or whatever in the DOM. That way if you create many you can do it so you only refresh the DOM once (refreshing the DOM is expensive)...