Aleks
Aleks

Reputation: 5854

Backbone view: el and events

I am trying to play around with Backbone and have trouble with Views, in particular with "el" and events. I noticed I'm not the first one, but unfortunatelly could not find an optimal answer to my questions.

My base relevant code:

HTML:

<!DOCTYPE html>
<html>
    <head>
        <script data-main="main" src="js/require.js"></script>
    </head>
    <body>
        <div id="login"></div>
    </body>
</html>

VIEW:

var view = Backbone.View.extend({
        el: $("#login"),

        initialize: function() {
            this.el.html('<div>click here</div>');         // TypeError: this.el.html is not a function 
        }
.....

Question 1:

Please note the comment in the initialize() function, that's error registered by Firebug when I load this code.

But, when I only remove the "el" config and instead put the corresponding assignement in initialize() the run-time error is gone!

var view = Backbone.View.extend({
            //  "el" is now removed 

        initialize: function() {
            this.el = $("#login");                       // explicit assignement
            this.el.html('<div>click here</div>');       // appends the div correctly
        }
.....

Question 2: In both of these examples if I specify events in View's events config, the event never fires:

events: {
            "click":          "tryLogin"
          },
          ...

Please enlighten me! :)

Upvotes: 0

Views: 1773

Answers (1)

user229044
user229044

Reputation: 239250

The specific problem you're having is that this.el is not a jQuery object, and it has no .html method. That's what this.$el is for; this.el is a raw DOM element and this.$el is the jQuery-wrapped version of that object. You should not overwrite el with a jQuery-selected element.

The real problem you're having is that you're trying to modify the DOM in initialize.

If, in initialize, you want to access a DOM element that is already in the DOM, you need to pass an el: option to the view's constructor. Generally you shouldn't do this though. Your view should do its DOM manipulation in its render method.

From the docs:

If you'd like to create a view that references an element already in the DOM, pass in the element as an option: new View({el: existingElement})

In practice I've found that el and $el are still available, but you should avoid using them in initialize regardless. Stick to modifying the DOM in render, which is specifically where your view's "drawing" code is meant to go:

var view = Backbone.View.extend({
  el: '#login',
  render: function () {
    this.$el.append('<h1>What!</h1>');
    return this;
  }
});


$(function () {
  new view().render();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js"></script>

<div id="login"></div>

In regards to Question 2, I cannot reproduce your problem. The following works exactly as expected:

var View = Backbone.View.extend({
  el: '#login',
  events: {
    'click': 'onClick'
  },
  onClick: function () {
    alert('click!');  
  },
  render: function () {
    this.$el.append('<h1>What!</h1>');
  }
});

new View().render();
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js"></script>

    <div id="login"></div>

Upvotes: 2

Related Questions