Backbone: Why Assign '$('#Footer')' to 'El'

Backbone: Why assign `$('#footer')` to `el`?

What is the difference between $el and el?

The el view property

this.el can be resolved from a DOM selector string or an Element;
otherwise it will be created from the view's tagName, className, id
and attributes properties. If none are set, this.el is an empty div,
which is often just fine.

It is a DOM element object reference. Do not set el directly, use the view.setElement method instead if you want to change it.

The $el property

A cached jQuery object for the view's element. A handy reference
instead of re-wrapping the DOM element all the time.

I like how user mu is too short puts it:

this.$el = $(this.el);

Also do not set $el directly, use the view.setElement method.

The el option

An el reference may also be passed in to the view's constructor.

new Backbone.View({ el: '#element' });
new Backbone.View({ el: $('#element') }); // unecessary

It overrides the el property, which is then used for the $el property.

If a selector string is passed, it is replaced with the DOM element it represents.

Why assign $('#footer') to el?

this.el can be a jQuery object. You can see that Backbone make sure el is a DOM element and $el is a jQuery object of it in the _setElement function:

_setElement: function(el) {
this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
this.el = this.$el[0];
},

This shows why this.$el is equivalent to $(this.el).

But what is Backbone.$?

Backbone keeps a reference to whatever is $.

For Backbone’s purposes, jQuery, Zepto, Ender, or My Library (kidding)
owns the $ variable.

In our case, $ is jQuery, so Backbone.$ is just jQuery, but Backbone dependencies are flexible:

Backbone's only hard dependency is Underscore.js ( >= 1.8.3). For
RESTful persistence and DOM manipulation with Backbone.View, include
jQuery ( >= 1.11.0), and json2.js for older Internet Explorer support.
(Mimics of the Underscore and jQuery APIs, such as Lodash and Zepto,
will also tend to work, with varying degrees of compatibility.)

this.$(selector) is equivalent to $(view.el).find(selector)

In fact, it's a little more efficient, the $ view function is just:

$: function(selector) {
return this.$el.find(selector);
},

What is a cached jQuery object?

In this case, it only means that a jQuery object is kept inside a variable, which is reused inside the view. It avoids the costly operation of finding the element with $(selector) each time.

You can (and should) use this little optimization whenever possible, like inside the render function:

render: function() {
this.$el.html(this.template(/* ...snip... */));
// this is caching a jQuery object
this.$myCachedObject = this.$('.selector');
},

onExampleEvent: function(e) {
// avoids $('.selector') here and on any sub-sequent example events.
this.$myCachedObject.toggleClass('example');
}

Prefixing the jQuery cached object variable with $ is just a standard, not a requirement.


Backbone's source code is less than 2000 lines, it's well-documented and easy to read. I highly encourage everyone to dive into it to easily understand the underlying logic.

They also offer an annotated source page which is even easier to read.

Additional reading

  • Start here: Backbone documentation
  • Introduction to Backbone
  • Backbone patterns
  • Best practices with Backbone

Is el having a ref or jquery reference ? code snippet below

update: el instanceof Backbone.$ mean check if el is jQuery/zepto object or not. Backbone.$ is backbone variable and used as alias for jQuery/zepto.

from the doc http://backbonejs.org/#View-dollar

If jQuery is included on the page, each view has a $ function that
runs queries scoped within the view's element. If you use this scoped
jQuery function, you don't have to use model ids as part of your query
to pull out specific elements in a list, and can rely much more on
HTML class attributes. It's equivalent to running:
view.$el.find(selector)

Backbone - this.$el vs $(this.el)?

If you call $(this.el), your just keep executing the jquery selector to get the same jquery object. '$el' is the cached version of $(this.el)

Backbone and jQuery Mobile : same header on all pages

Okay, I've finally found the solution by using trigger event.

I just want same header and footer on each page (or template) by using Backbone and jQuery Mobile. This is in fact pretty simple but I was on the wrong way when I researched.

The following is 2 simples applications ; the only difference is here :

The normal app (working jsFiddle : http://jsfiddle.net/QLu4P/)

app.Views.Main = Backbone.View.extend({
initialize : function(params)
{
// ...
},

render : function()
{
// ...
$(this.el).html(renderedContent);
// ...
}

});

The app using jQuery Mobile (working jsFiddle : http://jsfiddle.net/q5TX7/)

app.Views.Main = Backbone.View.extend({
initialize : function(params)
{
// ...
},

render : function()
{
// ...
$(this.el).html(renderedContent).trigger('create');
// ...
}

});

I've just added .trigger('create') on $(this.el).html(renderedContent) !
I've purposely put jQuery Mobile tags on the 2 apps in order to highlight that this is almost the same code.

Hope it gonna be useful.

What is the difference between $el and el in Backbone.js views?

"el"  is HTMLElement
"$el" is jQuery

lets say that you do this

var myel = this.el; // here what you have is the html element, 
//you will be able to access(read/modify) the html
//properties of this element,

with this

var my$el = this.$el; // you will have the element but 
//with all of the functions that jQuery provides like,
//hide,show etc, its the equivalent of $('#myel').show();
//$('#myel').hide(); so this.$el keeps a reference to your
//element so you don't need to traverse the DOM to find the
// element every time you use it. with the performance benefits
//that this implies.

one is the html element and the other is the jQuery object of the element.

Need help understanding the basics of nested views in backbone

Since your "shell" or "layout" view never changes, you should render it upon application startup (before triggering any routes), and render further views into the layout view.

Let's say your layout looked something like this:

<body>
<section id="layout">
<section id="header"></section>
<section id="container"></section>
<section id="footer"></section>
</section>
</body>

Your layout view might look something like this:

var LayoutView = Backbone.View.extend({
el:"#layout",
render: function() {
this.$("#header").html((this.header = new HeaderView()).render().el);
this.$("#footer").html((this.footer = new FooterView()).render().el);
return this;
},

renderChild: function(view) {
if(this.child)
this.child.remove();
this.$("#container").html((this.child = view).render().el);
}
});

You would then setup the layout upon application startup:

var layout = new LayoutView().render();
var router = new AppRouter({layout:layout});
Backbone.history.start();

And in your router code:

var AppRouter = Backbone.Router.extend({
initialize: function(options) {
this.layout = options.layout;
},

home: function() {
this.layout.renderChild(new HomeView());
},

other: function() {
this.layout.renderChild(new OtherView());
}
});

There are a number of ways to skin this particular cat, but this is the way I usually handle it. This gives you a single point of control (renderChild) for rendering your "top-level" views, and ensures the the previous element is removed before new one is rendered. This might also come in handy if you ever need to change the way views are rendered.

how to set the another view objects in backbone.js

The code is heavily inspired by my other answer, but you need to read the Backbone documentation (and also Underscore's) to understand what's going on, what you're doing and what needs to be done to add features to it. Otherwise, you'll always drift away from the working code into a new mess that you don't understand.

The Backbone doc is short and sweet and the source code is filled with comments. Don't read all of Underscore's doc, but read at the least the documentation for the functions you're using (like _.template).


The Router routes

Since you copy-pasted the exact code from my other answer without first checking if it works, you copied a mistake that I made. The more specific routes should be defined first and the catch-all route last.

routes: {
'about': 'aboutRoute',
'contact': 'contactRoute',
// put the catch-all last
'*home': 'homeRoute',
},

When instantiating a new Router, its constructor calls _bindRoutes which parses routes from the last to the first.

_bindRoutes: function() {
if (!this.routes) return;
this.routes = _.result(this, 'routes');
var route, routes = _.keys(this.routes);
while ((route = routes.pop()) != null) {
this.route(route, this.routes[route]);
}
},

Routes added later may override previously declared routes.


The render function

What do you think the following will do?

render: function(){
var template = _.template("<strong>About page</strong>");
}

So all that's doing now is:

  • it creates a new function,
  • puts it in a local variable named template
  • and do nothing else.

or even this:

my_template: _.template("<strong>Contact page</strong>");

my_template property on views is not standard Backbone, so if you do nothing with it, it won't do something by itself.

To understand, you need to know that the Underscore _.template function takes a template string as argument (and optionally a settings object) and returns a new pre-compiled template function which takes an object as an argument. (More info and examples)

Backbone view's render function is left to the developer to override. It should ideally render something and be idempotent.

A simple view could be:

views.About = Backbone.View.extend({
template: _.template("<strong>About page</strong>"),
render: function() {
this.$el.html(this.template());
return this;
}
});

A good convention is to return this at the end of render to enable
chained calls.

And you should be doing this because you're chaining calls in your code:

content.render().el

jQuery and el

Why do you pass this.hideSpinner()?

this.$content.html(content.render().el, this.hideSpinner());

While it do change the HTML of the #content div with the view's el, using this.hideSpinner() returned value as a second parameter makes no sense.

jQuery's .html function only takes one parameter.


Where to put a loading spinner?

Not within synchronous DOM manipulation. It's useless to put a spinner that before being seen is removed.

A loading spinner makes sense when there is asynchronous loading of some sort, and that you want to inform the user that the page hasn't crashed or froze.

Say you want to load news for the home page.

homeRoute: function() {
console.log("showing the spinner");
this.layout.showSpinner();

var collection = new Backbone.Collection({
url: "/you/api/url/for/latest/news" // simple example
}),
view = new views.Home({
collection: collection
});

collection.fetch({
context: this,
success: function() {
console.log("removing the spinner");
this.layout.hideSpinner();
this.layout.setContent(view);
}
});

console.log("End of homeRoute function");
},

In the console, you'll see the logs in the following order:

showing the spinner
End of homeRoute function
removing the spinner

More information, see:

  • what's the difference between el and $el and what's a cached jQuery object
  • JavaScript inheritance, Making copy of objects and Chaining


Related Topics



Leave a reply



Submit