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

StackOverflow https://stackoverflow.com/questions/7157235

Question

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?

Était-ce utile?

La solution

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)...

Autres conseils

Use this in your View .it will unbind the click events

initialize : function() {
    $(this.el).unbind("click");
}

Just a thought that creating a Backbone.View for each and every button in your app could be a performance overkill and you can't leverage the "delegate" feature in jQuery. I'd instead create a Backbone.View for the parent element of those buttons instead.

Of course, if you have a few special buttons with complicated logic then they probably do deserve their own View classes. :)

Give your buttons unique ids, for example <button id="button1"> and <button id="button2">, then in your events hash, you need to specify the click event and the id of the button you want to handle that event for, e.g:

events : {
        "click #button1" : "onClick",
        "click #button2" : "doSomethingElse"
    }

Now this will call onClick() only when you click on the button with id=button1 and call doSomethingElse() when you click on the button with id=button2

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top