98

How should a view's el be handled? It has to be set, otherwise events don't fire (see here).

But should it be an element that is already on the page? In my app, I render a (jQuery Templates) template into a Fancybox. What should the el be in that case?

Community
  • 1
  • 1
Manuel Meurer
  • 3,238
  • 6
  • 35
  • 50

3 Answers3

122

A views el is where all the event binding takes place. You don't have to use it but if you want backbone to fire events you need to do your rendering work on the el. A views el is a DOM element but it does not have to be a pre-existing element. It will be created if you do not pull one from your current page, but you will have to insert it into the page if you ever want to see it do anything.

An example: I have a view that creates individual items

window.ItemView = Backbone.View.extend({
    tagName: "li", //this defaults to div if you don't declare it.
    template: _.template("<p><%= someModelKey %></p>"),
    events: {
         //this event will be attached to the model elements in
         //the el of every view inserted by AppView below
        "click": "someFunctionThatDoesSomething"
    },
    initialize: function () { 
        _.bindAll(this, "render");
        this.render();
    },
    render: function () {
        this.el.innerHTML = this.template(this.model.toJSON());
        return this;
    }
});
window.AppView = Backbone.View.extend({
    el: $("#someElementID"), //Here we actually grab a pre-existing element
    initialize: function () { 
        _.bindAll(this, "render");
        this.render(new myModel());
    },
    render: function (item) { 
        var view = new ItemView({ model: item });
        this.el.append(view.render().el);
    }
});

The first view just creates the list items and the second view actually places them on the page. I think this is pretty similar to what happens in the ToDo example on the backbone.js site. I think convention is to render you content into the el. So the el serves as a landing place or a container for placing your templated content. Backbone then binds its events to the model data inside of it.

When you create a view you can manipulate the el in four ways using el:, tagName:, className:, and id:. If none of these are declared el defaults to a div without id or class. It is also not associated with the page at this point. You can change the tag to something else by using using tagName (e.g. tagName: "li", will give you an el of <li></li>). You can set the id and class of el likewise. Still el is not a part of your page. The el property allows you to do very fine grain manipulation of the el object. Most of the time I use an el: $("someElementInThePage") which actually binds all the manipulation you do to el in your view to the current page. Otherwise if you want to see all the hard work you have done in your view show up on the page you will need to insert/append it to the page somewhere else in your view (probably in render). I like to think of el as the container that all your view manipulates.

Darryl Hein
  • 142,451
  • 95
  • 218
  • 261
LeRoy
  • 3,195
  • 4
  • 26
  • 23
  • Thanks for the clarification and the example! I think it is not always clear what the el should be in certain situations and the developer has to get a "feel" for it. Your explanations certainly helped! One question, though: you don't define an el in your ItemView, but you access it in the render function. Will this work? Is there some kind of default el if you don't explicitly define it? – Manuel Meurer Apr 23 '11 at 07:48
  • 3
    By default el is a "div" tag with no id or class. It is a DOM object not bound to your page DOM. Usually I insert/append it to the page in the render function or the render function of a parent view. – LeRoy Apr 23 '11 at 15:42
  • Updated my answer with some description of the properties that define the el. – LeRoy Apr 23 '11 at 20:43
  • 5
    I guess my only issue with grabbing a pre-existing element with el: $("#someElementID") is your view probably knows more than it should, making it difficult to reuse it. see "Decouple view from DOM..." http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/ – Scott Coates May 04 '12 at 16:02
  • @scoarescoare since you are adding the el property on instantiation the view itself does not actually know more than it should, it remains modular and able to accept any $(el) you want to hand it. So this makes it quite reusable, really. – Metagrapher Jun 13 '12 at 20:16
  • @Metagrapher - check out the blog link I supplied. It describes how this view is quite coupled to the DOM making reusability difficult. Consider if another page uses a different ID such as #someElementID2" – Scott Coates Jun 14 '12 at 20:44
  • The AppView is coupled to the DOM, yes, but the ItemView is not. I think I was reading this incorrectly on the first go. Instead of putting the el declaration in the definition of AppView, it should be in the instantiation of the AppView. Of course, you can always override the default by defining it yourself on instatiation. I don't think this practice is that bad. If you have a page that uses the same elem id, then just call for a different one. It's good to provide a default. – Metagrapher Jun 21 '12 at 17:02
  • It would probably be better to define a className and a tagName though – Metagrapher Jun 21 '12 at 17:05
  • Since you're calling render inside `ItemView`'s initialize fn, is there any need to call `view.render()` inside `AppView`'s render? – vikki Apr 02 '14 at 08:44
  • yeah, behind the scenes, Backbone creates an element node basically ``this.el = document.createElement(this.tagName);``` so imagine that your render function is actually doing this: ````render: function() { this.el = document.createElement(this.tagName); this.el.innerHTML = this.template(this.context); return this; },```` – Elise Chant Jan 28 '16 at 02:32
6

Bit old now, but I was confused as well, and so for other people that get here, this fiddle might help - http://jsfiddle.net/hRndn/2/

var MyView = Backbone.View.extend({

    events: {
        "click .btn" : "sayHello",
    },

    sayHello : function() {
        alert("Hello");
    },


    render : function() {
        this.$el.html("<input type='button' class='btn' value='Say Hello'></input>");

    }
});

$(function() {
    myView = new MyView({el:"#parent_id"});
    myView.render();
});
sth
  • 222,467
  • 53
  • 283
  • 367
Mark
  • 1,754
  • 3
  • 26
  • 43
  • I've followed this model and wish I had not. To destroy a view and remove the bindings, backbone's convention is view.remove(). That destroys $el and removes it from the DOM, so when you need to show the view again, $el does not exist. – J.J. Apr 17 '13 at 14:48
  • @Mark what If i'm using composite architecture like marionette.... do i still need to have view's el? – afr0 Jun 07 '13 at 10:31
  • your fiddle code doesn't work, as link should be http://jsfiddle.net/hRndn/ – Izzy Jul 12 '16 at 07:24
  • @Mahi Yes you are right. I probably pasted wrong link, sorry. This one works: http://jsfiddle.net/hRndn/127/ – Izzy Nov 22 '16 at 09:42
1

You want your 'el' to reference an element that contains a child element that has any event that triggers a change in your view. Could be as wide as a "body" tag.

joshvermaire
  • 1,476
  • 1
  • 12
  • 17
  • Hmm, that doesn't really clear it up either. Most of the times the elements that could trigger a change in my view are inside the template the view renders, so before it is rendered these elements don't exist yet. – Manuel Meurer Apr 13 '11 at 19:17