Clean of any inline logic or special tags
CSS selectors are used to bridge the HTML with Javascript actions
Providing a radical separation between the representation and the logic
We have been happily using pure.js since 2008 for our own web app, BeeBole Timesheet
Why don't you try it too?
pure.js can be used where Javascript and DOM are available
It can be run server side, but shines client side
If a Javascript library is present in the page( jQuery, dojo, domassistant, mootools, prototype, sizzle or sly )
pure.js will automatically extend it with the method described here below.
<div> | |
Hello <span></span> | |
</div> |
var data = { | |
who:'BeeBole!' //the JSON data | |
}, | |
directive = { | |
'span':'who' //make the link between the HTML SPAN tag and the JSON property "who". | |
}; | |
$( 'div' ).render( data, directive ); //render the result |
<div> | |
Hello <span>BeeBole!</span> | |
</div> |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<!-- HTML template --> | |
<div> | |
Hello <span></span> | |
</div> | |
<script> | |
var data = { | |
'who': 'BeeBole!' | |
}, | |
directive = { | |
'span': 'who' //look for the SPAN, set its text to the value of property 'who' | |
}; | |
//render the template | |
$p( 'div' ).render( data, directive ); | |
</script> | |
</body> | |
</html> |
A directive, is the instruction you give to render a template
It is a JSON object, with the format {CSS_selector: action}
pure.js will do that action on the node matching the CSS_selector
Here are the type of actions you can do on HTML nodes and attributes:
{ 'a': 'who' }
Will assign the value of the property who from the JSON data, to the text of the a tag
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<!-- HTML template --> | |
<div> | |
Hello <span></span> | |
</div> | |
<script> | |
var data = { | |
'who': 'BeeBole!' | |
}, | |
directive = { | |
'span': 'who' //look for the SPAN, set its text to the value of property 'who' | |
}; | |
//render the template | |
$p( 'div' ).render( data, directive ); | |
</script> | |
</body> | |
</html> |
To assign the value of an attribute, we use @ at the end of the selector
{ 'a@href': 'url' }
Will assign the value of the property url from JSON data, to the href of the a tag
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<!-- HTML template --> | |
<form> | |
<input type="submit" /> | |
</form> | |
<script> | |
var data = { | |
'caption': 'Ok' | |
}, | |
directive = { | |
'input@value': 'caption' //note the @attr notation to select an attribute | |
}; | |
$p( 'form' ).render( data, directive ); | |
</script> | |
</body> | |
</html> |
Using <- in a directive, we can repeat a node
From the HTML template:
<ul> | |
<li> | |
<span></span> | |
</li> | |
</ul> |
{ | |
animals:[ | |
{name: 'mouse'}, | |
{name: 'cat'}, | |
{name: 'bird'}, | |
{name: 'dog'} | |
] | |
} |
{ | |
'li':{ | |
'animal<-animals':{ | |
'span': 'animal.name' | |
} | |
} | |
} |
[ | |
{name: 'mouse'}, | |
{name: 'cat'}, | |
{name: 'bird'}, | |
{name: 'dog'} | |
] |
{ | |
'li':{ | |
'animal<-':{ | |
'span': 'animal.name' | |
} | |
} | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<!-- HTML template --> | |
<ul> | |
<li> | |
<span></span> | |
</li> | |
</ul> | |
<script> | |
var data = { | |
animals:[ | |
{name: 'mouse'}, | |
{name: 'cat'}, | |
{name: 'bird'}, | |
{name: 'dog'} | |
] | |
}; | |
var directive = { | |
'li':{ | |
'animal<-animals':{ //for each entry in animales name the element 'animal' | |
'span': 'animal.name' //the dot selector, means the current node (here a LI) | |
} | |
} | |
}; | |
$p( 'ul' ).render( data, directive ); | |
</script> | |
</body> | |
</html> |
You can use a Javascript function as the action
This brings you all the flexibility of Javascript to render your templates
{ | |
'span':function( a ){ | |
return this.name; | |
} | |
} |
An argument is passed to that function, called a here below:
{ | |
'span':function( a ){ | |
return a.pos + '. ' + a.item.name; //concatenate the row number, a dot and the property who | |
} | |
} |
*The last 3 properties(*) are only present when the directive is inside a loop
When you load pure.js in your page, a global variable called $p is available
Here is a description of its methods:
compile takes an HTML node found by selector and returns a Javacript function
Provide your JSON data as the argument of that function, and you will get a string of the HTML result
var compiled = $p( '.template' ).compile( directive ); | |
document.querySelector( '.result' ).innerHTML = compiled( data ); |
render takes the JSON data, and a directive(or its compiled function) as arguments
And replace the node found on selector
// For single rendering, use a directive | |
$p( '.result' ).render( data, directive ); | |
// For multiple rendering, use a compiled function | |
$p( '.result' ).render( data, compiled ); |
Here above, the node found by the selector .result, will be replaced by the new HTML
Note: If you plan to render multiple times a template, eg: when the data change, you must use the compiled version instead of using the directive
autoRender automatically maps the JSON data to the class attributes in the HTML
There is no need to give directive for the rendering
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<!-- HTML template --> | |
Hello <a class="who site@href" href="#"></a> | |
<script> | |
$p( 'body' ).autoRender({ | |
who:'BeeBole!', | |
site:'http://beebole.com' | |
}); | |
</script> | |
</body> | |
</html> |
In the example above, there are two classes for the A tag
who will give the value of its text
While site@href will put the value of site as its href attribute
Note: If the property is an array, the node will be repeated
Consider autoRender just for basic rendering. You can't format or use functions with it
In addition, if forces you to have a relative coherence between both structures( your data and the HTML )
A directive allows a better abstraction
pure.js detects if you are using a JS framework in the same page(dojo, domassistant, jquery, mootools, prototype, sizzle or sly) and adds automatically the 3 methods above to your familiar environment
i.e: with jQuery, you can render a template by calling $( selector ).render( data, directive )
We tried to keep the syntax minimal and standard
Using only HTML, CSS selectors and JSON
However some additional notations were introduced to cover some specific cases.
Here they are:
A node that you repeat may be empty. To access its node text value, you can use the . as the selector
<ul> | |
<li></li> | |
</ul> |
'li':{ | |
'animal<-animals':{ | |
'.':'animal.name', // the . selects the node that is repeated, here the LI | |
'@data-legs': 'animal.legs' //to set an attribute to the node, just use @ followed by the attribute name | |
} | |
} |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<!-- HTML template --> | |
<ul> | |
<li></li> | |
</ul> | |
<script> | |
var data = { | |
animals:[ | |
{name:'bird', legs: 2 }, | |
{name:'cat', legs: 4 }, | |
{name:'dog', legs: 4 }, | |
{name:'mouse', legs: 4} | |
] | |
}, | |
directive = { | |
'li':{ | |
'animal<-animals':{ | |
'.':'animal.name', // the . selects the node that is repeated, here the LI | |
'@data-legs': 'animal.legs' //to set an attribute to the node, just use @ and the attribute name | |
} | |
} | |
}; | |
$p( 'ul' ).render( data, directive ); | |
</script> | |
</body> | |
</html> |
By default, a selected node text or attribute will be replaced by the value coming from the data
The + is useful if you want to keep the existing value of the node text or attribute, and just add something before or after it
The directive below will append the value of the property email
And put it as the href attribute of the node matching the selector .email
<div> | |
<a class="email" href="mailto:">Contact</a> <!-- href has already a value --> | |
</div> |
var data = { | |
email: 'pure@beebole.com' | |
}, | |
directive = { | |
'.email@href+': 'email' // append pure@beebole.com to "mailto:" | |
}; |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<div> | |
<a class="email" href="mailto:">Contact</a> <!-- href has already a value --> | |
</div> | |
<script> | |
var data = { | |
email: 'pure@beebole.com' | |
}, | |
directive = { | |
'.email@href+': 'email' // append pure@beebole.com to "mailto:" | |
}; | |
// render the template | |
$p( 'div' ).render( data, directive ); | |
</script> | |
</body> | |
</html> |
The directive below will concatenate the properties site and page separated by a /
Then set the href attribute of the node matching the selector .site
<div> | |
<a class="site">Web site</a> | |
</div> |
var data = { | |
site: 'http://beebole.com', | |
page: 'pure' | |
}, | |
directive = { | |
'.site@href': '#{site}/#{page}' // concatenate site and page separated by a "/" | |
}; |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>PURE Unobtrusive Rendering Engine</title> | |
<script src="http://pure.github.io/pure/libs/pure.js"></script> | |
</head> | |
<body> | |
<div> | |
<a class="site">Web site</a> | |
</div> | |
<script> | |
var data = { | |
site: 'http://beebole.com', | |
page: 'pure' | |
}, | |
directive = { | |
'.site@href': '#{site}/#{page}' // concatenate site and page separated by a "/" | |
}; | |
// render the template | |
$p( 'div' ).render( data, directive ); | |
</script> | |
</body> | |
</html> |
var directive = { | |
'li':{ | |
'animal<-animals':{ | |
'span.name': 'animal.name' | |
}, | |
// at the same level of the <- loop declaration | |
// use the keyword: sort | |
sort: function(a, b){ // same kind of sort as the usual Array sort | |
var cmp = function(x, y){ | |
return x > y? 1 : x < y ? -1 : 0; | |
}; | |
return cmp( a.name, b.name ); | |
} | |
} | |
}; |
The "a" and "b" parameters are items of the array
The function will be used as a usual Array.sort( function( a, b ){ ... }) style
The "sort" function will work only when you loop on an array.
If you add a sort function on an object properties loop, pure.js will throw an exception.
var directive = { | |
'li':{ | |
'animal<-animals':{ | |
'span.name':'animal.name' | |
}, | |
// at the same level of the <- loop declaration | |
// use the keyword: filter | |
filter:function(a){ | |
// if the name starts with "a" or "A", keep it, returns true | |
// otherwise, skip it, return false | |
return ( /^a/i ).test( a.item.name ); | |
} | |
} | |
}; |
The a parameter of the function is the same object that is passed to loops directive functions( with the properties context, pos, item, items )
In the example above only the items with a name starting with "a" or "A" will be kept
If the function returns true the item is kept, if false the item won't be rendered
The filter works on both arrays and object properties loops
Notes: a.pos will continue to indicate the current index of the innermost loop, even if items are skipped
The node to loop must have a parent node, the template root can't be looped