BackboneJs
and
Web Components
Mario Jurić
Software Analyst
@
Topics
- Intro to Web Components
- (Very) basic BackboneJs overview
- Demo app showing the integration between the two
Web Components
- Templates
- Shadow DOM
- Custom Elements
- HTML Imports
Templates
-
Its content is effectively inert until activated. Essentially, your markup is hidden DOM and does not render.
-
Any content within a template won't have side effects. Script doesn't run, images don't load, audio doesn't play,...until the template is used.
-
Templates are basically a document fragment
-
activate with document.ImportNode( sometemplate.content, true )
Shadow DOM
-
Hide Presentation Details
-
Separating Content from Presentation
-
Advanced Projection
-
Multiple shadow roots in the same shadow host
Custom Elements
-
Define new HTML/DOM elements
-
Create elements that extend from other elements
-
Logically bundle together custom functionality into a single tag
-
Extend the API of existing DOM elements
HTML Imports
-
include HTML documents in other HTML documents
-
Sub-imports
-
Content is useful only when you add it
-
resources are loaded only once
-
Imports block rendering of the main page
-
Imports don't block parsing of the main page
Backbone.js
-
Backbone.Model
-
Backbone.Collection
-
Backbone.Router
-
Backbone.Events
-
Backbone.View
Backbone.View
// Backbone.View
// -------------
// Backbone Views are almost more convention than they are actual code. A View
// is simply a JavaScript object that represents a logical chunk of UI in the
// DOM. This might be a single item, an entire list, a sidebar or panel, or
// even the surrounding frame which wraps your whole app. Defining a chunk of
// UI as a **View** allows you to define your DOM events declaratively, without
// having to worry about render order ... and makes it easy for the view to
// react to specific changes in the state of your models.
// Creating a Backbone.View creates its initial element outside of the DOM,
// if an existing element is not provided...
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
options || (options = {});
_.extend(this, _.pick(options, viewOptions));
this._ensureElement();
this.initialize.apply(this, arguments);
this.delegateEvents();
};
// Cached regex to split keys for `delegate`.
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
// List of view options to be merged as properties.
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
// Set up all inheritable **Backbone.View** properties and methods.
_.extend(View.prototype, Events, {
// The default `tagName` of a View's element is `"div"`.
tagName: 'div',
// jQuery delegate for element lookup, scoped to DOM elements within the
// current view. This should be preferred to global lookups where possible.
$: function(selector) {
return this.$el.find(selector);
},
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function(){},
// **render** is the core function that your view should override, in order
// to populate its element (`this.el`), with the appropriate HTML. The
// convention is for **render** to always return `this`.
render: function() {
return this;
},
// Remove this view by taking the element out of the DOM, and removing any
// applicable Backbone.Events listeners.
remove: function() {
this.$el.remove();
this.stopListening();
return this;
},
// Change the view's element (`this.el` property), including event
// re-delegation.
setElement: function(element, delegate) {
if (this.$el) this.undelegateEvents();
this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
this.el = this.$el[0];
if (delegate !== false) this.delegateEvents();
return this;
},
// Set callbacks, where `this.events` is a hash of
//
// *{"event selector": "callback"}*
//
// {
// 'mousedown .title': 'edit',
// 'click .button': 'save',
// 'click .open': function(e) { ... }
// }
//
// pairs. Callbacks will be bound to the view, with `this` set properly.
// Uses event delegation for efficiency.
// Omitting the selector binds the event to `this.el`.
// This only works for delegate-able events: not `focus`, `blur`, and
// not `change`, `submit`, and `reset` in Internet Explorer.
delegateEvents: function(events) {
if (!(events || (events = _.result(this, 'events')))) return this;
this.undelegateEvents();
for (var key in events) {
var method = events[key];
if (!_.isFunction(method)) method = this[events[key]];
if (!method) continue;
var match = key.match(delegateEventSplitter);
var eventName = match[1], selector = match[2];
method = _.bind(method, this);
eventName += '.delegateEvents' + this.cid;
if (selector === '') {
this.$el.on(eventName, method);
} else {
this.$el.on(eventName, selector, method);
}
}
return this;
},
// Clears all callbacks previously bound to the view with `delegateEvents`.
// You usually don't need to use this, but may wish to if you have multiple
// Backbone views attached to the same DOM element.
undelegateEvents: function() {
this.$el.off('.delegateEvents' + this.cid);
return this;
},
// Ensure that the View has a DOM element to render into.
// If `this.el` is a string, pass it through `$()`, take the first
// matching element, and re-assign it to `el`. Otherwise, create
// an element from the `id`, `className` and `tagName` properties.
_ensureElement: function() {
if (!this.el) {
var attrs = _.extend({}, _.result(this, 'attributes'));
if (this.id) attrs.id = _.result(this, 'id');
if (this.className) attrs['class'] = _.result(this, 'className');
var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
this.setElement($el, false);
} else {
this.setElement(_.result(this, 'el'), false);
}
}
});
Demo time
How I see them working
- Web components (custom elements) should try so solve one problem and do it right
- Backbone has simple but powerful concepts to bring structure to a set of separate components
- Backbone has a very (very!) slim view layer and 3/4 Web Components pieces fill that gap
Ten Principles for Great General Purpose Web Components
- Address a common need.
- Do one job really well.
- Work predictably in a wide variety of circumstances.
- Be useful right out of the box.
- Be composable.
- Be styleable.
- Be extensible.
- Think small.
- Adapt to the user and device.
- Deliver the key benefit to HTML authors, not just coders.
... and some of mine
-
Create a clear input/output API for your components
-
DOCUMENT the API
-
Use custom events Backbone (and others) can easily connect to
FAQ
Reference
BackboneJs and Web Components - make 'em dance
By jurza
BackboneJs and Web Components - make 'em dance
- 1,523