Shaven is a DOM building utility and template engine based on JsonML. This simple markup language lets you easily represent XML/HTML with JSON and plain JavaScript objects. Templates are therefore simply a tree of nested arrays.
['#demo',['h1#logo', 'Test', {title: 'Test'}],['p', 'Some example text'],['ul#list.bullets',['li', 'item1'],['li.active', 'item2'],['li',['a', 'item3', {href: '#'}],],],]
compiles to
id="demo"id="logo" title="Test"TestSome example textid="list" class="bullets"item1class="active"item2href="#"item3
The first element of the array is the root html element which is going to contain all the child-elements. Those are arrays themselves and are structured as follows:
On the server you can install Shaven with npm:
npm install shaven
Then require it in your scripts like this:
import shaven from 'shaven'
In the browser use a bundler or load the pre-compiled version of Shaven from npmjs.com/package/shaven
src="shaven.js"
Now you can start using it by passing an array to Shaven:
const shavenObject = shaven(['p', 'some text'])
There are three different methods to add a DOM-fragment to a document.
shaven([document.body,['div#demo',…],])
To get the HTML-fragment from the Shaven-object reference it with
.rootElement
.
const shavenObject = shaven(['#demo',…])document.body.appendChild(shavenObject.rootElement)
Both compiles to:
id="demo"…
innerHTML
of an element.To get the HTML string of a Shaven-object simply call the
toString()
method, convert the object to a String
with String(shavenObject)
or the easiest way -
add it to another string.
console.log('The HTML for the object is ' + shavenObject)document.body.innerHTML = shavenObject.toString()
When you omit the html-tag it uses the default div
tag.
So div#container
ist the same as
#container
['#container', ['p', 'Test']]
compiles to
id="container"Test
['p#example', 'example text']
compiles to
id="example"example text
['p.info', 'example text']
compiles to
class="info"example text
When an element with an id is created it automatically gets inserted into the corresponding property in the return-object of the shaven function call. Therefore it can get easily referenced later on.
const shavenObject = shaven([document.body,['div#test', 'some text'],])doSomething(shavenObject.ids.test)
Reference an element without an id by using the $
keyword
to assign it a reference-name.
const shavenObject = shaven([document.body,['div$test', 'some text'],])doSomething(shavenObject.references.test)
All entries of an object become the attributes of the element.
If an object entry is undefined
the attribute is
set to an empty string.
If an entry is null
or false
the attribute
is not created.
If the style entry is an object it will automatically get compiled
to a string. This is especially useful for working with svgs, where
inline styles are still frequently used.
Values which are false
, null
or
undefined
are deleted from the final style-string.
['p', 'Hello', {class: 'new focus','data-lang': 'de',title: false,style: {color: 'red',display: 'block',font: null,},}]
compiles to
class="new focus" data-lang="de" style="color:red;display:block"Hello
If a transform attribute is an an array of objects it will get compiled to an SVG transform string.
['svg',{width: 50, height: 50},['circle', {r: 5,transform: [{type: 'translate', x: 4, y: 5},{type: 'skewX', x: 6},],}],]
compiles to
width="50" height="50"r="5" transform="translate(4,5) skewX(6)"
HTML-strings are escaped by default.
['div', '<p>This is <em>HTML</em></p>']
compiles to
<p>This is <em>HTML</em></p>
To disable escaping add a !
at the end of the
element-tag string.
['div!', '<p>This is <em>HTML</em></p>']
compiles to
This is HTML
When an element array contains an undefined
content-value
or no content-value at all, the element will get compiled with an
empty body.
When an element contains a null
or a false
value,
the element won't get compiled at all. This is handy for disabling
parts
of templates or ensuring that no empty container-elements get
rendered.
(A true
value does therefore not change the
compilation.)
An array which consists exclusively of subarrays will get merged into the current array
['p', [['span', 'One'],['span', 'Two'],['span', 'Three'],]]
compiles to
OneTwoThree
In order to create reusable templates it's best practice to create a
function
which accepts a data object, inserts the data into the JsonML array
and
returns it.
Finally iterate over the contacts and use one of the two insertion
methods
to build the DOM with the contacts.
function contactTemplate (data) {return ['div.contact',['h1', data.name],['dl',['dt', 'Age:'],['dd', data.age],['dt', 'Mobile:'],['dd', data.mobile],['dt', 'Email:'],['dd', data.email],]]}contacts.forEach(contact =>shaven([document.body,contactTemplate(contact),]))
You can provide a callback function, which will be called after the element has been created, with the element as the only argument. This functionality is especially suited to add event-listeners.
function doSomething (element) {alert(element + ' was created')}shaven([document.body,['div', doSomething],])
Shaven can be configured at module scope and settings can be overwritten at each invocation. Available settings are:
{// Wrap all attribute values in quotesquoteAttributes: true,// Character used as quotation markquotationMark: '"',// Escape HTML in text if not explicitly defined otherwiseescapeHTML: true,// Convert array of transform objects to transform-stringconvertTransformArray: true,// Default namespacenamespace: 'xhtml',// Automatically identify namespaceautoNamespacing: true,}
To overwrite the default settings use setDefaults()
.
This will be set at the module scope for every invocation of shaven.
import shaven from 'shaven'shaven.setDefaults({quoteAttributes: false,})
To overwrite the settings for only one element use the object invocation mode of shaven:
shaven({quoteAttributes: false,elementArray: ['p', 'Example text', {title: example}],})
compiles to
title=exampleExample text
Shaven automatically uses the correct namespaces to create HTML, SVG, and MathML. It is, however, also possible to use a custom namespace to create documents in various other XML-based languages or to overwrite the defaults.
shaven({namespace: 'http://www.mozilla.org/xbl',elementArray: ['page',['vbox', {flex: 1}],],})
compiles to
flex="1"