Powerful and modern data binding (like Angular, Ember, ...) + templating + DOM manipulation (like jQuery, Zepto, jqLite, ...) in only 23kb minified (7kb gzipped).
Bindux in a few words:
- Near of native JS and HTML5 / CSS3.
- DOM manipulation (if you prefer, you can use Bindux with jQuery).
- Native data-binding, Bindux use
Object.observe()
, you can use a polyfill for old browsers. - Some useful helpers to manipulate the DOM, data-binding, values, HTML, ...
- Standart compliant, no need for <%=foo%> or {{foo}} assignments.
- Templating based on HTML, HTML is a great markup langage and natively evaluated by the browser.
- Not brilliant meta language, hieroglyphic or any other symbol to remember. Only HTML we already know.
- DSL-free (Domain Specific Languages), if needed you can use with any template engine.
- Note: DOM templating with Domain Specific Languages (suck as <%=foo%> or {{foo}}) is SLOW. All template engines require parsing and rendering phases.
- Easy to use
- Extensible
This is the alpha version, the documentation is in progress.
Download dist/bindux.min.js.
Bindux expose the variables bindux
and a shortcut ux
.
// true
console.log(bindux === ux);
bindux.noConflict()
to redefine in your sauce:
var ui = bindux.noConflict();
// now `ui` is `bindux` object
console.log(ui);
// now is undefined
console.log(bindux);
// now is undefined
console.log(ux);
Documentation in progress, the following is some quick examples:
ux.dom('div').css('color', 'green');
// or jQuery like
var $ = ux.dom;
$('div').css('color', 'green');
Some methods:
var $ = ux.dom;
var elements = $('div');
elements.attr('href', newLink);
elements.html('<b>great</b>');
elements.text('some text');
elements.append('<b>html</b>');
elements.appendText('text');
elements.appendChild(node);
elements.prepend('<b>html</b>');
elements.prependText('text');
elements.prependChild(node);
elements.insertBefore('added before');
elements.insertAfter('added after');
elements.parent();
elements.findParent('.someClass');
elements.addClass('btn-success');
elements.toggleClass('some-class');
elements.show();
elements.hide();
elements.on('click', listener);
elements.off('click', listener);
elements.trigger('click', data);
elements.css({
color: '#333',
// or 'background-color': '#f1f1f1'
backgroundColor: '#f1f1f1'
});
// each node
elements.each(function() {
$(this).next().addClass('selected');
this.remove();
});
Traverse the tree of node
and keep only the text nodes:
var textNodes = $.traverse(node, NodeFilter.SHOW_TEXT);
Observe DOM mutations and bind with the items
object:
var items = {};
$.observe({
// element
el: {
el: $('#list').get(),
observer: function(mutations, observer, context) {
console.log(mutations, observer, context);
}
},
// object
obj: {
obj: items,
observer: function(changes, context) {
console.log(changes, context);
}
}
});
... and more, see the API in the code source.
TODO: doc
Binders are used to make the link between the JS code and the HTML document.
Like the AngularJS directives (ng-repeat, etc).
Each binding is linked by an attribute and an object (named the scope
).
All binding attributes are prefixed by n-
(n = node). You can change the prefix like this:
ux.opt.binder.prefix = 'ux-';
All scopes are stored in ux.scopes
object.
If a property passed from HTML to a binder is a function, then it will be executed and its return value will be used by the binder.
Init the binding with ux.init()
:
// define the controllers of your application
ux.ctrls.header = headerCtrl;
ux.ctrls.navBar = navBarCtrl;
ux.ctrls.chat = chatCtrl;
ux.ctrls.article = articleCtrl;
ux.ctrls.user = userCtrl;
// initialize the binding
ux.init();
The controller which manages the scope.
The use of controllers is not mandatory, you can define directly a scope ux.scopes.something
but the controllers are convenient for code organization.
When defining a controller, a scope of the same name is automatically created if it does not exist.
HTML
<div n-ctrl="myCtrl"></div>
JS
/**
* My controller
*
* @param {object} scope The scope to handle.
* @param {Element} el The node element of the controller
*/
ux.ctrls.myCtrl = function(scope, el) {
};
Like ng-repeat
of AngularJS, the binder each
instantiates a template once per item from a collection. Each template instance gets its own scope, where the given loop variable is set to the current collection item.
HTML
<div n-ctrl="myCtrl">
<ul>
<li n-each="message messages" n-html="message"></li>
</ul>
</div>
If you prefer the Angular way (message in
messages):
<div n-ctrl="myCtrl">
<ul>
<li n-each="message in messages" n-html="message"></li>
</ul>
</div>
JS
scope.messages = [];
scope.messages.push('Hello');
scope.messages.push('Hi');
It's also possible to use an object or an array of objects.
Example with an array
of object
:
HTML
<div n-ctrl="myCtrl">
<ul>
<li n-each="user users">
<span n-text="user.name"></span>
<span n-text="user.email"></span>
<p n-html="user.bio"></p>
</li>
</ul>
</div>
JS
scope.users = [];
scope.users.push({
name : 'Nico',
email : 'contact@domain.tld',
bio : 'I eat <strong>pizza</strong>, miam!'
});
scope.users.push({
name : 'Sarah Connor',
email : 'contact@fear.ltd',
bio : 'I am the mom of the future o_O I eat <strong>bullets</strong>!'
});
Handles directly an element.
HTML
<div n-ctrl="myCtrl">
<img n-el="src:logoUrl">
</div>
JS
scope.logoUrl = '/assets/img/logo.png';
Use only a callback which manages the element:
HTML
<div n-ctrl="myCtrl">
<div n-el="elHandler"></div>
</div>
JS
scope.elHandler = function(data) {
// current element
var node = data.node;
console.log(data);
};
Add the property value performed as HTML.
HTML
<div n-ctrl="myCtrl">
<div n-html="contents"></div>
</div>
JS
scope.contents = '<strong>Hello world</strong>';
Displays:
<div n-html="contents"><strong>Hello world</strong></div>
Add the property value performed as text. The HTML tags are natively escaped by the browser (TextNode).
HTML
<div n-ctrl="myCtrl">
<div n-text="contents"></div>
</div>
JS
scope.contents = '<strong>Hello world</strong>';
Displays:
<div n-text="contents"><strong>Hello world</strong></div>
Binds an event listener on the element using the event specified.
Attribute value: event:listener
.
HTML
<div n-ctrl="myCtrl">
<button on="click:onClick">Click me!</button>
</div>
JS
scope.onClick = function(event) {
$(this).text('clicked :)');
};
Define the scope to bind in the HTML. It's useful to bind another scope easily.
HTML
<div n-ctrl="a">
<span n-text="scope a"></span>
</div>
<div n-ctrl="b">
<span n-scope="a" n-text="scope a"></span>
<span n-text="scope b"></span>
</div>
<!-- Without controller -->
<div n-scope="b">
<!-- Uses the scope "b" -->
<div n-html="user.name"></div>
</div>
Transform a value. Example, HELLO to Hello:
JS
scope.val = 'HELLO';
HTML
<span n-text="val|lc|ucFirst"></span>
List:
- lc Make a string lowercase
- lcFirst Make a string's first character lowercase
- uc Make a string uppercase
- lcFirst Make a string's first character uppercase
- rmSpace Remove space
- escape Escape a string (replace HTML tags and some character by HTML entities)
- e Shortcut of escape filter
- unescape Converts the HTML entities in escaped string (with escape / e filter), to their corresponding characters
Create a new filter:
ux.filters.strong = function(str) {
return '<strong>' + str + '</strong>';
};
Then use:
<span n-text="val|strong"></span>
Parsing:
var value = ux.expr.parse(expression, scope);
Note: expression
is passed to all parsers (by default Bindux have only one parser pipe
).
use a specific parser:
var value = ux.expr.parser('pipe').parse(expression, scope);
Create a new parser:
ux.expr.parsers.something = function(expression, scope, context) {
// parse ...
return str;
};
Note: context
is an optional argument to pass context data.
Then use:
var value = ux.expr.parser('something').parse(expression, scope, context);
When calling ux.expr.parse()
, all parsers are used (also the new parser something
).
If needed, you can pass the context
:
var value = ux.expr.parse(expression, scope, context);
Bindux inherits of evemit.
ux.on('say-hello', function(hello) {
console.log(hello); // Hello World!
});
ux.emit('say-hello', 'Hello World!');
Methods:
- ux.on(event, fn, [context])
- ux.once(event, fn, [context])
- ux.emit(event, [...arg])
- ux.off(event, fn)
- ux.listeners([event])
See the doc of Evemit.
Emitted by ux.init()
on preloading.
Emitted by ux.init()
on loading.
Emitted by ux.init()
on postloading.
Emitted when a scope object was changed.
Example a scope defined with ux.scopes.user
emit scopes.user.changes
.
Emitted on preloading of a controller.
Example a controller defined with ux.ctrls.user
emit ctrls.user.preLoad
.
Emitted on prebooting of a controller.
Emitted on postbooting of a controller.
Emitted by ux.binders.each
when a property (or an array
key) of a scope was changed.
Example, consider a scope named app
(ux.scopes.app
):
Scope object:
// if this code is in a controller,
// `scope` variable is passed by argument
var scope = ux.scopes.app;
scope.users = {
nico: {
job: 'developer',
age: '32',
country: 'France',
avatar: '/img/avatars/default.png'
}
};
Binder:
<ul>
<li n-each="user in users"></li>
</ul>
When scope.users
changes:
scope.users.nico.avatar = '/img/avatars/tux.png';
The event emitted is binders.each.scopes.app.users.changes
In a application it is common to manipulate the colors via hexa and RGB(A).
The ux.color
object is an handy helper to facilitate the transition from one to the other.
Convert a given color
to RGB(A).
// rgb(255,255,255)
ux.color.toRgb('#ffffff');
// rgb(255,255,255)
ux.color.toRgb('white');
// rgb(255,255,255)
ux.color.toRgb('255,255,255');
// rgb(255,255,255)
ux.color.toRgb('#fff');
// rgb(255,255,255,0.8)
ux.color.toRgb('#fff', 0.8)
Convert a given color
to Hexa.
// #ffffff
ux.color.toHex('#fff');
// #ffffff
ux.color.toHex('white');
// #ffffff
ux.color.toHex('rgb(255,255,255)');
// #ffffff
ux.color.toHex('rgb(255,255,255,0.8)');
// #ffffff
ux.color.toHex('rgba(255,255,255,0.8)');
Some color names.
// #008000
console.log(ux.color.names.green);
// see all colors
console.log(ux.color.names);
Get the first HTML tag.
Returns the tag (lowercase) if found, null
otherwise.
var attr, str;
str = 'some text before <div id="layout" class="active">' +
'<span class="something">contents</span>' +
'</div> some text after';
attr = ux.html.getFirstTag(str);
// div
console.log(attr);
Get attributes from a string. Returns attribute(s) name=value pairs in object.
var attrs, str;
str = '<div id="layout" class="active">' +
'<span class="something">contents</span>' +
'</div>';
attrs = ux.html.getAttrs(str);
// Object {id: "layout", class: "active"}
console.log(attrs);
Convert a given string / HTML to Node
instance (Element
or TextNode
).
var node;
// TextNode
// (node.nodeType === Node.TEXT_NODE)
node = ux.html.toNode('hello');
console.log(node.data);
// Element (strong)
// (node.nodeType === Node.ELEMENT_NODE)
// (node.nodeName === 'STRONG')
node = ux.html.toNode('<strong>hello</strong>');
console.log(node.innerHTML);
Returns the string (str
) without HTML tag, or only with allowed
tag(s).
Remove all tags:
// Who are you
ux.html.stripTags('<p>Who</p> <br /><b>are</b> <i>you</i>');
Remove all tags except <i>
and <b>
:
// Who <b>are</b> <i>you</i>
ux.html.stripTags('<p>Who</p> <br /><b>are</b> <i>you</i>', '<i><b>');
Bindux is near of the native features and standart compliant. Bindux is based on the native capabilities of standarts supported by the major modern browsers: Chrome 29+, Firefox 24+, Opera 24+, IE 11+, Safari 6.1+ and browsers based on its.
No hack imposed to the modern browsers, so it's very well for the performance. The hacks are only necessary for the old browsers and should be handled independently.
For make that the old browsers support the modern features you can use polyfills:
- es5-shim, provides ES5 (EcmaScript 5)
- DOM4, provides DOM4 spec
- es6-collections, provides WeakMap, Map and Set
Bindux is developped in ES6 (EcmaScript6) in src directory and compiled to ES5 in the dist/bindux.min.js file.
Build a minified distributable, in the terminal:
gulp
Develop with the automatic rebuilds (the distribulable and the unit tests):
gulp dev
Bindux is unit tested with Unit.js and Mocha.
TODO: There are still things to test.
MIT (c) 2014, Nicolas Tallefourtane.
Bindux is designed and built with love by
Nicolas Talle |