Backbone.View extension for convenient work with elements, css classes and selectors. Inspired by i-bem.js
It's very common situation when we need to use the same selector in different parts of our view. Considering selectors can be complex it's not a good practice to mention them more than once. Using Backbone.View.Elements you can adjust selectors in one place like this:
_selectors: function () {
return {
elemName: '.block__elem-name'
};
}
then in the code you can get the selector using the _selector
method
this._selector('elemName'); // returns '.block__elem-name'
but the more often situation is when you need to retrive an element by the selector
this._elem('elemName'); // returns jQuery collection found by '.block__elem-name'
Furthermore the _elem
method caches the results so you need not to save elements to properties of views. Another
advantage is when an HTML template is changed you have to change JS in only one place.
The same situation with CSS selectors - we don't want to duplicate them. Classes are placed inside _classes
method:
_classes: function () {
return {
className: '.block__elem_some_class'
};
}
then in the code you can get the class using the _class
method
this._class('className'); // returns 'block__elem_some_class'
or selector for the class
this._selector('className'); // returns '.block__elem_some_class'
or elements collection
this._elem('className'); // returns jQuery collection found by '.block__elem_some_class'
But more often we need to do some manipulations with classes like adding a class to an element. The _addClass
method
exists for this reason
this._addClass('className', 'elemName'); // add the 'block__elem_some_class' to elements found by '.block__elem-name'
See also _removeClass
, _hasClass
, _toggleClass
and some others bellow.
TOC generated with DocToc
To install latest version just type:
bower install backbone.view.elements --save
If you do not have bower:
npm install -g bower
You have two ways to include the script
- Add script tag with Backbone.View.Elements right after Backbone like this:
<script src="path/to/backbone.js"></script>
<script src="path/to/Backbone.View.Elements.js"></script>
- Inherit your view from
Backbone.ElementsView
var MyView = Backbone.ElementsView.extend({
// yout view prototype here
});
Using AMD loader, for example RequireJS
- Add info about jQuery and Backbone locations to the shim
requirejs.config({
paths: {
underscore: 'path/to/underscore',
backbone: 'path/to/backbone',
jquery: 'path/to/jquery'
},
shim: {
jquery: {
exports: 'jQuery'
}
}
});
- If you are using old versions of backbone and underscore, you must declare
exports
anddeps
for them:
shim: {
jquery: {
exports: 'jQuery'
},
underscore: {
exports: '_'
},
backbone: {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
}
}
- Describe your view depending on Backbone.View.Elements and extend it:
require(['path/to/Backbone.View.Elements'], function (ElementsView) {
var MyView = ElementsView.extend({
// your view prototype here
});
});
You can use following properties and methods inside your child classes
- type
{jQuery}
Cached window
object wrapped to jQuery/Zepto
- type
{jQuery}
Cached document
object wrapped to jQuery/Zepto
- type
{jQuery}
Cached document.body
object wrapped to jQuery/Zepto
- type
{Object}
Data attributes of this.$el
- returns
{Object.<string>}
Place here CSS classes used in a View. Method should return an object, which keys are readable class names and values are CSS classes. You can extend this method in child classes.
Consider your declaration is:
_classes: function(){
return {
activeItem: 'very-long-css-class-for-active-state-for-some-item'
};
}
Then you can get the class this way:
this._class('activeItem');
or selector for the class
this._selector('activeItem'); // returns dot + the class
or even elements with the class
this._elem('activeItem');
Please note the _elem
method caches results, so if you dynamically set the class to
different elements use _findElem
instead
- arguments:
{String}
name
Key from_classes
{...string|object}
[placeholders]
values for placeholders, see examples
- returns
{String}
CSS class - throws
{Error}
if the name does not match any key in_classes
or value for the key is empty
Returns CSS class by its name. Classes are described in _classes
.
Suppose we have classes described like this:
_classes: function(){
return {
activeItem: 'item_active_yes',
itemOfType: 'item_type_%s',
namedItem: 'item-%(name)s'
};
}
Then in code we can get the class this way:
this._class('activeItem'); // item_active_yes
this._class('itemOfType', 'apple'); // item_type_apple
this._class('namedItem', {name: 'note'}); // item-note
- arguments
{String|Array.<String>}
cls
class name, if you want to use placeholders pass the array (see examples){String|Array.<String>|jQuery}
[elem=this.$el]
element name, checks the root element if not specified
- returns
{Boolean}
Checks the element has CSS class described in _classes
.
Example: checking Backbone.View.$el
has a class specified in _classes
as active
this._hasClass('active')
Checking some child element has a class specified in _classes
. For retrieving
element the _elem
method is used
this._hasClass('active', 'someItem')
Usage with placeholders
this._hasClass(['namedItem', 'itsName'], 'elemName')
- arguments:
{String|Array.<String>}
cls
class name, if you want to use placeholders pass the array (see examples){String|Array.<String>|jQuery}
[elem=this.$el]
element name, adds to the root element if not specified
- returns
{jQuery}
Add CSS class described in _classes
to element.
For example if we want to add the class specified in _classes
as active
to Backbone.View.$el
:
this._addClass('active')
Adding a class specified in _classes
to some child element. For retrieving
element the _elem
method is used
this._addClass('active', 'someItem')
Usage with placeholders
this._addClass(['namedItem', 'itsName'], 'elemName')
- arguments:
{String|Array.<String>}
cls
class name, if you want to use placeholders pass the array (see examples){String|Array.<String>|jQuery}
[elem=this.$el]
element name, removes from the root element if not specified
- returns
{jQuery}
Remove CSS class described in _classes
from an element. To remove class from from the Backbone.View.$el
this._removeClass('active')
Removing a class specified in _classes
from some child element. For retrieving an element the _elem
method is used
this._removeClass('active', 'someItem')
Usage with placeholders
this._removeClass(['namedItem', 'itsName'], 'elemName')
- arguments:
{String|Array.<String>}
cls
class name, if you want to use placeholders pass the array (see examples){String|Array.<String>|jQuery}
[elem=this.$el]
element name, toggles to the root element if not specified{Boolean}
[toggle]
flag to add or remove the class
- returns
{jQuery}
Toggles CSS class described in _classes
on element. Example: toggling a class specified in _classes
as active
on Backbone.View.$el
this._toggleClass('active', true);
Toggling a class specified in _classes
on some child element. For retrieving element the _elem
method is used
this._toggleClass('active', 'someItem', false)
Usage with placeholders
this._toggleClass(['namedItem', 'itsName'], 'elemName', true)
- returns
{Object.<string>}
Place here selectors used in a View. Method should return an object, which keys are readable selector names and values are CSS selector. You can extend this method in child classes.
Routine use:
_selectors: function(){
return {
firstLevelItem: '.list>.item'
};
}
Then you can get the selector this way:
this._selector('firstLevelItem')
or elements selected by it
this._elem('firstLevelItem')
Using with placeholders
_selectors: function(){
return {
itemById: '.item[data-id=%s]',
namedItem: '.item-%(name)s'
};
}
then in code
this._selector('itemById', 3); // .item[data-id=3]
this._elem('namedItem', {name: 'note'}); // finds child elements by .item-note selector
- arguments:
{string}
name
Key from_selectors
{...string|object}
[placeholders]
values for placeholders, see examples
- returns
{String}
CSS selector - throws
{Error}
if the name does not match any key in_selectors
and_classes
Returns CSS selector by its name. Selectors are described in _selectors
Example: suppose we have selectors described like this:
_selectors: function(){
return {
firstLevelItem: '.list>.item',
itemById: '.item[data-id=%s]',
namedItem: '.item-%(name)s'
};
}
Then in code we can get the class this way:
this._selector('firstLevelItem') // .list>.item
this._selector('itemById', 'apple') // .item[data-id=apple]
this._selector('namedItem', {name: 'note'}) // .item-note
Using at Backbone.View#events
events: function(){
var events = {};
events['click ' + this._selector('firstLevelItem')] = this._onItemClick;
return events;
}
- arguments:
{String}
name
Selector name
- returns
{Boolean}
Returns true if selector with the name is descried in _classes
or _selectors
- arguments:
{string}
name
The name of searching element{...string|object}
[placeholders]
values for placeholders, see examples
- returns
{jQuery}
Returns jQuery or Zepto collection of elements by the name described in
_selectors
or _classes
. Caches th results so you don't need to
remember them to properties. Use _dropElemCache
to clean caches
var Page = ElementsView.extend({
_classes: function () {
return {
popup: 'my-class-for-popups'
};
},
_selectors: function () {
return {
popupByName: '.popup[data-name=%s]'
};
},
initialize: function () {
ElementsView.prototype.initialize.apply(this, arguments);
this._elem('popupByName', 'greeting').show();
var $allPopups = this._elem('popup');
}
});
- arguments:
{string}
name
{...string|object}
[placeholders]
- returns
{jQuery}
Finds element without using cache
var SomeElement = ElementsView.extend({
_classes: function () {
return {
activeDropdown: 'dropdown_active'
};
},
_selectors: function () {
return {
dropdownByN: '.dropdown:eq(%s)'
};
},
initialize: function () {
ElementsView.prototype.initialize.apply(this, arguments);
this._addClass('activeDropdown', ['dropdownByN', 2]);
var $activeDropdown = this._elem('activeDropdown'); // caches activeDropdown
this._removeClass('activeDropdown', ['dropdownByN', 2]);
this._addClass('activeDropdown', ['dropdownByN', 3]);
// how to get active dropdown? _elem returns dropdown with number 2 because of the cache
$activeDropdown = this._findElem('activeDropdown'); // ignores the caches
}
});
- arguments:
{String}
[name]
The name of the element whose cache will be cleaned up. If it's absent the whole cache will be dropped
Clears the cache for ElementsView._elem
- arguments:
{String}
name
The name of the searching element{String}
[attr]
The attribute name, if it's absent all attributes will be returned as object
- returns
{*|Object}
Returns the data attribute value by the name of element described in _selectors
or
_classes
and the name of attribute itself. If you need data attributes of the root
element just use the _data
property