Data-binding + Templating + DOM manipulation
JavaScript HTML CSS
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
dist
src
tasks
test
.gitignore
.jshintrc
.npmignore
LICENSE
README.md
gulpfile.js
package.json

README.md

Bindux - Bind the User eXperience

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.

Install

Download dist/bindux.min.js.

Usage

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);

DOM

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.

Binders

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();

ctrl

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) {

};

each

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>!'
});

el

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);
};

html

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>

text

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">&lt;strong&gt;Hello world&lt;/strong&gt;</div>

on

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 :)');
};

scope

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>

Filters

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>

Parser

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);

events

Bindux inherits of evemit.

ux.on('say-hello', function(hello) {
  console.log(hello); // Hello World!
});

ux.emit('say-hello', 'Hello World!');

Methods:

See the doc of Evemit.

Internal events

preLoad

Emitted by ux.init() on preloading.

load

Emitted by ux.init() on loading.

postLoad

Emitted by ux.init() on postloading.

scopes.{scope name}.changes

Emitted when a scope object was changed.
Example a scope defined with ux.scopes.user emit scopes.user.changes.

ctrls.{controller name}.preLoaded

Emitted on preloading of a controller.
Example a controller defined with ux.ctrls.user emit ctrls.user.preLoad.

ctrls.{controller name}.preBoot

Emitted on prebooting of a controller.

ctrls.{controller name}.postBoot

Emitted on postbooting of a controller.

binders.each.scopes.{scope name}.{property name}.changes

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

ux.color

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.

toRgb(color, alpha)

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)

toHex(color)

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)');

names

Some color names.

// #008000
console.log(ux.color.names.green);

// see all colors
console.log(ux.color.names);

ux.html

getFirstTag(str)

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);

getAttrs(str)

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);

toNode(str)

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);

stripTags(str, allowed)

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>');

Browser support

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:

Development of Bindux core

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

Unit tests

Bindux is unit tested with Unit.js and Mocha.

TODO: There are still things to test.

LICENSE

MIT (c) 2014, Nicolas Tallefourtane.

Author

Bindux is designed and built with love by

Nicolas Tallefourtane - Nicolab.net
Nicolas Talle
Make a donation via Paypal