Backbone: Why assign `$('#footer')` to `el`?
What is the difference between $el
and el
?
The el
view property
It is a DOM element object reference. Do not set
this.el
can be resolved from a DOM selector string or an Element;
otherwise it will be created from the view'stagName
,className
,id
andattributes
properties. If none are set,this.el
is an emptydiv
,
which is often just fine.
el
directly, use the view.setElement
method instead if you want to change it.The $el
property
I like how user mu is too short puts it:A cached jQuery object for the view's element. A handy reference
instead of re-wrapping the DOM element all the time.
Also do not setthis.$el = $(this.el);
$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:This shows why_setElement: function(el) {
this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
this.el = this.$el[0];
},
this.$el
is equivalent to $(this.el)
.But what is Backbone.$
?
Backbone keeps a reference to whatever is $
.In our case,For Backbone’s purposes, jQuery, Zepto, Ender, or My Library (kidding)
owns the$
variable.
$
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 thisvar 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 thisvar 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 remove
d 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.
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;
}
});
And you should be doing this because you're chaining calls in your code:A good convention is to
return this
at the end ofrender
to enable
chained calls.
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
Getting Access Is Denied Error on IE8
Jquery Attr('Checked','Checked') Works Only Once
Understanding Xmlhttprequest Over Cors (Responsetext)
Intercept Paste Event in JavaScript
How to Replace Last Occurrence of Characters in a String Using JavaScript
What's "This" in JavaScript Onclick
Sampling a Random Subset from an Array
Why Firefox Says That Window.Event Is Undefined? (Call Function with Added Event Listener)
Properties of JavaScript Function Objects
Saving a Text File on Server Using JavaScript
How to Know When All Promises Are Resolved in a Dynamic "Iterable" Parameter
Invoke a Callback at the End of a Transition