jiesa

0.0.7-c • Public • Published

jiesa

Coverage Status

Jiesa is a low level JavaScript Virtual DOM library containing a collection of modules designed to provide a declarative way of representing the DOM for your Javascript application. It's safe to use Jiesa in production, and it's features include...

  • DOM Level 0 events
  • Server side rendring facilities
  • SVG, MathML, and Custom Elements & type extension support
  • Namespaces support (e.g., SVG, MathML)
  • SVG 1.1 support
  • LifeCycle Hooks
  • Mounting and unmounting ala REACT
  • Smart algorithm to handle attributes and properties
  • Smart algorithm to handle CSS style properties with 'px' suffix support, and vendor prefixes
  • Delegated event system
  • Option to create your own virtual nodes
  • Easy to understand API

Virtual nodes

Supported virtual nodes:

  • Element
  • Text

jiesa.Element()

jiesa.Element() create a real DOM node, and accepts an optional data object and an optional string or array of children. E.g. new jiesa.Element("div", {}, [new Text("Heeey!")])

The optional data object contains this configurable values:

  • key
  • props
  • attrs
  • events
  • hooks

key

Keys are unique and are attached to a virtual node, to ensure that only the elements which were really changed are updated. Even if elements were added/removed before or moved to another position. While common operations like appending and prepending nodes are optimized, it is also very fast at updating random changes like sorting or filtering. It can take a normal text string, or a numger

// text
{ key: "hello" }
 
// number
{ key: 123 }

props

props are a short hand for properties, and Jiesa supports all HTML properties. And it uses a smart algorithm to figure out what you are doing, and set the correct property, boolean property, boolean attribute or attribute if needed.

Example you are setting a style value, but it will be visible as a attribute.

props: { style: { display: "none" } }
// equal to
attrs: { style: { display: "none" } }

Other examples:

 // 'id' property
{ props: {id: "123"} };
 
 // `className` property
{ props: { className: "andrew" } };
 
 // `checked` property
{ props: { checked: true } };
 

attrs

Same behaviour as for props, but for HTML attributes. A few examples:

 // img element with src and alt attributes
{ attrs: {src: 'http://...', alt: 'Image ...'}};
 
 // id and class
{ attrs: {id: "name-field", "class":"'important-field"}};
 

CSS style properties are also set through attributes, and can set as key / value pair, or array of values.

 // key / value pair
{ attrs: {style: { 'border-bottom': '1px solid black', color: 'gray'}} };
 
// array of values
attrs: { style: { cursor: ["crosshair", "auto"] } }
 

events

See the event section.

hooks

See Lifecycle hooks section.

Virtual nodes created with jiesa.Element() have it's own public API methods you can use as well.

  • .render() - render the virtual node, and return a single DOM element
  • .toHtml() - create HTML markup server-side
  • .patch() - patch a virtual node with a real DOM node
  • .detach() - Remove a real DOM element from where it was inserted
  • .equalTo() - Checks if two virtual nodes are equal to each other, and they can be updated

All of this API methods requires a argument - a DOM node - except for .patch(), .toHtml(). This method require a virtual DOM node object.

jiesa.Text()

jiesa.Text()create a virtual text node, and it takes only one argument. E.g. new jiesa.Text("Hello, Andrew!!");

In the same way as jiesa.Element(), jiesa.Text() support it's own public API methods you can use:

  • .render() - render the virtual node, and return a single DOM element
  • .toHtml() - create HTML markup server-side
  • .patch() - patch a virtual node with a real DOM node
  • .detach() - Remove a real DOM element from where it was inserted
  • .equalTo() - Checks if two virtual nodes are equal to each other, and they can be updated

Lifecycle hooks

Various methods are executed at specific points in a virtual node's lifecycle. This hooks are executed at different steps.

To use hooks, pass them as an object to hook field of the data object argument.

{ hooks: {
    detach: function() {},
    destroy: function() {}
  } 
}

Following Lifecycle hooks are supported:

  • updated
  • created
  • detach
  • destroy

Create hook

The hook are called once a virtual node has been created

Update hook

The hook are called every time an update occur on a virtual node

Detach hook

The hook are triggered when an element is directly being removed from the DOM.

Destroy hook

The hook is called once a virtual node is to be removed from the DOM and it's parent container – not if it is the child of an element that is removed. For that see the Detach hook.

SVG and MathML

Both SVG and MathML are supported, and will work right out of the box.

Example:

// create a virtual SVG node
new jiesa.Element("svg", { attrs: { cx: 1, cy: 2 } });
 

Jiesa.Tree()

The API provides all necessary functions to create, update and remove virtual DOM nodes to/from the real DOM.

mount()

Mounting to the document.body.

var tree = new jiesa.Tree(),
   foo = new jiesa.Element("h1", {}, [new jiesa.Text("Foo visited Bar")]);
tree.mount(document.body,  foo);

Mounting to element with an id - #mount-point.

tree.mount("#mount-point",  foo);

Mounting to element with a class - .mount-point.

tree.mount(".mount-point",  foo);

Mounting to element with DOM element - document.getElementById("test").

tree.mount(document.getElementById("test"),  foo);

You have probably realized that it supports CSS Selectors.

You can also mount with a factory - function.

var tree = new jiesa.Tree(),
Text = new jiesa.Text(),
render = (function() {
 
   var children = [new Text("Hello"), new Text("World!!")];
 
   return new Element("div", {}, children);
}())
 
// Mount the tree
 tree.mount("#mount-point", render);

With jiesa you also got more advanced options such as mounting with a unique ID identifer.

var bookedId = tree.guid();
var mountId = tree.mount("#mount-point1", new Element("div", {}, ["#1", "#2", "#3"]), {mountId: bookedId})

unmount()

When you unmount a virtual tree, you can choose to unmount them all, or only one tree. Note that the unmount() function needs to be called with the mount identifier.

var tree = new jiesa.Tree();
tree.unmount(mountID); // unmount a virtual tree with the mount identifier
 
tree.unmount(); // unmount all virtual trees

update()

Once a virtual tree is mounted, you can update it. This API method takes one or two arguments. If no virtual node are set - as the second argument - it will only update already mounted node. E.g. changing / updating it's focus, or diff / patch the child nodes.

var tree = new jiesa.Tree();
 tree.update( uid); // update already virtual tree

With two arguments, you can update the existing node with another virtual node.

// create and mount a virtual node
var tree = new jiesa.Tree();
var Element = new jiesa.Element();
var Text = new jiesa.Text();
var foo = new Element("h1", null, [new Text("Foo visited Bar")])
var mountId = tree.mount(document.body,  foo);
 
// update the tree with the mount identifier
tree.update(mountId, new Element("h1", null, [ new Text("Bar was eating Foo")]));

Update all mounted trees at once:

tree.update();

Diff / patch

You can patch virtual nodes like this:

nodeA.patch( nodeB);

Focus for input elements are maintained only when needed so it will have no impact on other diff / patch operations.

var tree = new jiesa.tree(),
    Element = jiesa.Element;
 
// ensuring right focus
var mountId,
    tree = new jiesa.Tree();
 
var from = [
    new Element("input", { key: 0, attrs: { id: "input_1", placeholder: "input_#1"}}),
    new Element("input", { key: 1, attrs: { id: "input_2", placeholder: "input_#2"}})
];
 
var to = [
    new Element("input", { key: 1, attrs: { id: "input_2", placeholder: "input_#2"}}),
    new Element("input", { key: 0, attrs: { id: "input_1", placeholder: "input_#1"}})
];
var active = false,
    updateFunc = function() {
    // negative value
    active = !active;
    return active ? from : to;
    },
    // mount
    mountId = tree.mount(document.body, updateFunc);
 
setInterval(function() {
    tree.update(mountId);
}, 1100);

Server side rendring (SSR)

Jiesa supports SSR out of the box, but differently then other virtual DOM implementations. E.g. REACT render all HTML markup as HTML properties.

With Jiesa - both HTML attributes and HTML properties are supported, and they are different by nature. So are the SSR. Attributes are treated as attributes, properties as properties.

However, there are a few exceptions:

  • viewBox - SVG attribute that has to be written as camelCase.
  • multiple - can be rendered as an property,not an attribute.
  • capture - can be rendered as an property, but are rendered as an attribute. If not, we are blocking for capture=camcorder", capture=microphone etc.

Example on setting capture attribute:

// as an HTML property
props: { type: "file", accept: "video/*;capture\=camcorder" }
// as an HTML attribute
attrs: { type: "file", accept: "video/*;capture\=camcorder" }
// outputs:  "<input type="file" accept="video/*;capture=camcorder"/>"

To do server rendring, use .toHtml() to turn virtual DOM nodes into HTML server-side. Properties get converted into attribute values.

// toHtml boolean properties
var element = new jiesa.Element("input", { props: {
    autofocus: true,
    disabled: false
    }
});
    html = element.toHtml();
 
// result:  <input autofocus>

If you pass in a boolean argument, the text strings will not be escaped.

 
.toHtml( true ); // output: "<&>"
 
.toHtml(); // outputs: "&lt;&amp;&gt;"
 

Differences between properties and attributes

The main difference between properties and attributes, are how boolean attributes are rendered server side.

 
// property
html = h( { tagName: "input", props: { checked : true, disabled : true } } );
 
// Output: "<input checked disabled/>"
 
// attribute
 
html = h( { tagName: "input", attrs: { checked : true, disabled : true } } );
 
// Output: "<input checked="" disabled=""/>"
 

With attributes you got more flexibility, and options. You can for example render your HTML markup so the output would be:

 
<input checked="checked" disabled="disabled"/>"
 

Event emitter

Jiesa is a cross-browser wrapper around the browser's native event, and each event are managed in a delegated way. The event handlers will be passed instances of SyntheticEvent, a cross-browser wrapper around the browser's native event. It has the same interface as the browser's native event, including stopPropagation() and preventDefault(), except the events work identically across all browsers.

To activate the event management, you wouldt need to initialize it with jiesa.initEvent() first. Like this:

jiesa.initEvent();
 
new jiesa.Element('div', {
    events: {
        onclick: function() {
            alert("Hello, world!");
        }
    }
});

After you have initialized the events, a set of common events are automatically bound to document body, and ready to be used.

You can use the events with or without the on prefix. E.g. onclick` or click. Jiesa are inteligent enough to figure out what you want.

Common event types

Trackira normalizes events so that they have consistent properties across different browsers and attach them to the current document. You controll this events with the bind() and unbind() API methods.

.listenTo() and .unlistenTo()

This API methods let you control the use of the common events.

// create virtual tree
var tree = new jiesa.Tree();
 
// initialize the events
var Evt = jiesa.initEvent();
 
// stop listen to the 'click' event
Evt.listenTo("click"); // try 'unlistenTo()' to unbind event
 
// the event will not work because you unbinded it
var vnode = new jiesa.Element("button", {
    events: {
        click: function () {
            alert("Hello, world!");
        }
    }
}, [new jiesa.Text("Click me!")]);
 
// mount element
tree.mount(document.body, vnode);

Other API methods

jiesa.detach()

This method let you remove virtual nodes.

Example on detaching / removing an element node:

var container = document.createElement("div");
var node = new jiesa.Element("div");
var element = node.render();
container.appendChild(element);
// container.childNodes.length equal to 1
 
node.detach();
// container.childNodes.length equal to 0

Support

Jiesa supports instant support. If any bugs reported, it will mostly be fixed in a few minutes after the bugs are reported. To help detection bugs, it's always nice with some code snippet or example code that can trigger the bugs you find.

Installing

Download the repo and run:

npm install

Commands:

Trackira uses gulp and Babel, and the commands are pretty straight forward:

// runs the unit tests
$ npm gulp
 
// build the library
$ npm gulp build
 
// bundle the library for our unit tests
$ npm gulp browserify
 
// show a istanbul coverage statistic on cmd line
$ npm gulp coverage
 
// runs unit tests
$ npm gulp test
 
// run the headless unit tests as you make changes
$ npm gulp watch
 
// set up a livereload environment for our spec runner
$ npm gulp test-browser

Contributing

If you like this/find it useful/find a bug please open an issue and, better yet, submit a Pull Request! ☺ All help appreciated, thanks!

Package Sidebar

Install

npm i jiesa

Weekly Downloads

6

Version

0.0.7-c

License

MIT

Last publish

Collaborators

  • kflash