Search code examples
javascriptjquerybackbone.jsbackbone-viewsbackbone-events

backbone.js: different output for same process


Working through the setElement part of Addy Osmani's backbone.js tutorial.

He presents this example:

// We create two DOM elements representing buttons
// which could easily be containers or something else
var button1 = $('<button></button>');
var button2 = $('<button></button>');

// Define a new view
var View = Backbone.View.extend({
      events: {
        click: function(e) {
          console.log(view.el === e.target);
        }
      }
    });

// Create a new instance of the view, applying it
// to button1
var view = new View({el: button1});

// Apply the view to button2 using setElement
view.setElement(button2);

button1.trigger('click'); 
button2.trigger('click'); 

However, he doesn't explain why there is different output for button1.trigger('click'); vs. button2.trigger('click'); -- possibly a dumb question, and I know that these are different ways of attaching the view to the button elements, but why does button2.trigger('click'); also return true?


Solution

  • button1.trigger('click'); shouldn't produce any output at all from that code.

    setElement is fairly simple:

    setElement view.setElement(element)

    If you'd like to apply a Backbone view to a different DOM element, use setElement, which will also create the cached $el reference and move the view's delegated events from the old element to the new one.

    So view.setElement(e) does four things:

    1. Unbinds the view's events from view.el.
    2. Sets view.el = e.
    3. Sets view.$el = Backbone.$(e).
    4. Binds the view's events to the new view.el.

    The result is that nothing will be left listening to events from button1 and view will be listening to events from button2.

    A more thorough example might help so let us attach some more click event handlers:

    var button1 = $('<button id="button1"></button>').click(function() {
        console.log('button1 jQuery', this);
    });
    var button2 = $('<button id="button2"></button>').click(function() {
        console.log('button2 jQuery', this);
    });
    
    var View = Backbone.View.extend({
        events: {
            click: function(e) {
                console.log('Backbone ', view.el, view.el === e.target);
            }
        }
    });
    
    var view = new View({el: button1});
    view.setElement(button2);
    
    button1.trigger('click'); 
    button2.trigger('click'); 
    

    Demo: http://jsfiddle.net/ambiguous/S7A9z/

    That should give you something like this in the console:

    button1 jQuery <button id=​"button1">​</button>​
    button2 jQuery <button id=​"button2">​</button>​
    Backbone <button id=​"button2">​</button>​ true
    

    Both raw jQuery event handlers will be triggered as expected but we'll only get the button2 event through Backbone because the setElement call happened before the trigger calls.

    So why is view.el === e.target true? Well, you're clicking on button2 so e.target will be button2 and the view.setElement(button2) call replaces view.el so this.el inside the Backbone click handler will also be button2.