pure.js - Javascript Template Engine

Write your templates in pure HTML

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

Here we explain why we did pure.js back in 2008
And you can ask your questions to the user group

Download pure.js on Github

We have been happily using pure.js since 2008 for our own web app, BeeBole Timesheet
Why don't you try it too?

Where to use pure.js?

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.

Here is a simple example

The template:

<div>
Hello <span></span>
</div>
view raw gistfile1.html hosted with ❤ by GitHub

How to render it:

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
view raw gistfile1.js hosted with ❤ by GitHub

The result:

<div>
Hello <span>BeeBole!</span>
</div>
view raw gistfile1.html hosted with ❤ by GitHub
Show a full page example
<!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>
view raw gistfile1.html hosted with ❤ by GitHub

Directive

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

Actions you can use in a directive

Here are the type of actions you can do on HTML nodes and attributes:

  • Assign the text value of a node

    { 'a': 'who' }
    Will assign the value of the property who from the JSON data, to the text of the a tag

    Show a full page example
    <!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>
    view raw gistfile1.html hosted with ❤ by GitHub
  • Assign the value of a node attribute

    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

    Show a full page example
    <!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>
    view raw gistfile1.html hosted with ❤ by GitHub
  • Make a loop

    Using <- in a directive, we can repeat a node

    From the HTML template:

    <ul>
    <li>
    <span></span>
    </li>
    </ul>
    view raw gistfile1.html hosted with ❤ by GitHub
    And the data:
    {
    animals:[
    {name: 'mouse'},
    {name: 'cat'},
    {name: 'bird'},
    {name: 'dog'}
    ]
    }
    view raw gistfile1.js hosted with ❤ by GitHub
    This directive:
    {
    'li':{
    'animal<-animals':{
    'span': 'animal.name'
    }
    }
    }
    view raw gistfile1.js hosted with ❤ by GitHub
    Will repeat the LI tag, for each entry in the array animals
    Naming an internal variable called animal
    Then will look for a span and fill it with the name of the animal

    Unnamed Array

    If your data is not an object but directly an array, like this:
    [
    {name: 'mouse'},
    {name: 'cat'},
    {name: 'bird'},
    {name: 'dog'}
    ]
    view raw gistfile1.js hosted with ❤ by GitHub
    There is no property name for the array, thus use directly nameOfItem<-
    {
    'li':{
    'animal<-':{
    'span': 'animal.name'
    }
    }
    }
    view raw gistfile1.js hosted with ❤ by GitHub
    Show a full page example
    <!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>
    view raw gistfile1.html hosted with ❤ by GitHub
  • Using a javascript function

    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;
    }
    }
    view raw gistfile1.js hosted with ❤ by GitHub
    The function will be called at render time, and return the value of the property name as the text value of the span
    this inside such function, can either be the root of your JSON data
    Or will represent the current element, if you are inside a loop

    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
    }
    }
    view raw gistfile1.js hosted with ❤ by GitHub
    It is an object with the following properties:
    • context : This is the full JSON that was passed for transformation
    • item* : the current loop item
    • items * : all the items of the loop
    • pos * : the current position in the loop. An integer when iterating an array, a property name iterating on a collection

    *The last 3 properties(*) are only present when the directive is inside a loop

The methods of the object $p

When you load pure.js in your page, a global variable called $p is available

Here is a description of its methods:
  • compile

    $p( selector ).compile( directive )

    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 );
    view raw gistfile1.js hosted with ❤ by GitHub
    In the example above, the HTML string is injected as the innerHTML of the node found on the selector .result

  • render

    $p( selector ).render( data, directive | compiledFunction )

    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 );
    view raw gistfile1.js hosted with ❤ by GitHub

    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

    $p( selector ).autoRender( data )

    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>
    view raw gistfile1.html hosted with ❤ by GitHub

    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

  • Use your favorite Javascript library

    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 )

Special notations in a 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:

  1. Access the current node repeated in a loop using a . ( dot ) as the selector

    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>
    view raw gistfile1.html hosted with ❤ by GitHub
    '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
    }
    }
    view raw gistfile1.js hosted with ❤ by GitHub
    Show a full page example
    <!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>
    view raw gistfile1.html hosted with ❤ by GitHub
  2. Prepend or append to a current value by using +

    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>
    view raw gistfile1.html hosted with ❤ by GitHub
    var data = {
    email: 'pure@beebole.com'
    },
    directive = {
    '.email@href+': 'email' // append pure@beebole.com to "mailto:"
    };
    view raw gistfile1.js hosted with ❤ by GitHub
    Show a full page example
    <!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>
    view raw gistfile1.html hosted with ❤ by GitHub
  3. Concatenate multiple values using #{ ... }

    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>
    view raw gistfile1.html hosted with ❤ by GitHub
    var data = {
    site: 'http://beebole.com',
    page: 'pure'
    },
    directive = {
    '.site@href': '#{site}/#{page}' // concatenate site and page separated by a "/"
    };
    view raw gistfile1.js hosted with ❤ by GitHub
    Show a full page example
    <!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>
    view raw gistfile1.html hosted with ❤ by GitHub
  4. How to sort a loop

    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 );
    }
    }
    };
    view raw gistfile1.js hosted with ❤ by GitHub

    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.

  5. How to filter out rows from a loop

    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 );
    }
    }
    };
    view raw gistfile1.js hosted with ❤ by GitHub

    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

Copyright © 2018 BeeBole