JSTemplates

JavaScript performance comparison

Revision 3 of this test case created by Andreas Deuschlinger

Preparation code

<script src="https://github.com/olado/doT/raw/master/doT.js"></script>

<script>
( function() {

        var _innerText = 'textContent';
        // using cc to target execution only for IE
        /*@cc_on
                @if ( @_jscript )
                        _innerText = 'innerText';
                @end
        @*/


        function children( dom ) {

                var childs = dom.children || dom.childNodes,
                        childs_out = [];

                for( var i=0, l=childs.length; i<l; i++ )
                        if( childs[i].nodeType === 1 )
                                childs_out.push(childs[i]);

                return childs_out;

        }

        var templateJS = function() {

                // templates cache
                var templates = {};

                templates.length = 0;

                templates.push = function( value ) {
                        this[this.length++] = value;
                };

                var traverseDOM = function( dom ) {

                        // create template
                        var tmpl = {
                                        dom: dom
                                },

                                k = 0,  // STACK counter

                                children_stack = [ children( dom ) ],           // Child STACK

                                // current STACK values
                                childs = children_stack[k],
                                i = [0],
                                l = [childs.length],

                                // curent STACK children counters
                                j = i[k],
                                n = l[k],

                                has_children,
                                child_obj,
                                current_dom,
                                keys = [],
                                shortcuts = {},         // this shortcuts will be available
                                shortcut_attr = "data-tmpl",
                                shortcut_name,
                                stack_switched = false;         // indicates that STACK has been switched


                        // check shortcut for root node
                        shortcut_name = dom.getAttribute(shortcut_attr);

                        if(shortcut_name) {
                                shortcuts[shortcut_name] = dom;
                                keys.push(shortcut_name);
                        }

                        // traverse all children of template
                        for(; j < n;) {

                                current_dom = childs[j];

                                // create shortcut
                                shortcut_name = current_dom.getAttribute(shortcut_attr);

                                if(shortcut_name) {
                                        shortcuts[shortcut_name] = current_dom;
                                        keys.push(shortcut_name);
                                }

                                has_children = children( current_dom );

                                // create new child object
                                if( !has_children.length ) {

                                        j++;

                                        // if not last children go up
                                        if( j===n && k > 0 ) {

                                                delete children_stack[k];
                                                delete i[k];
                                                delete l[k];

                                                --k;
                                                stack_switched = true;
                                        }

                                } else {

                                        // increase STACKS
                                        children_stack.push( has_children );

                                        i.push(0);
                                        l.push( has_children.length );

                                        // save last STACK children counter
                                        i[k] = j+1;

                                        ++k;

                                        stack_switched = true;

                                }

                                // checkout switched stack data
                                if( stack_switched ) {
                                        childs = children_stack[k];
                                        j = i[k];
                                        n = l[k];

                                        stack_switched = false;
                                }
                        }

                        // add keys
                        tmpl.keys = keys;

                        // add shortcuts to template
                        tmpl.shortcuts = shortcuts;

                        // push template to cache
                        templates.push( tmpl );

                        return templates.length - 1;

                };

                return {

                        getTemplate: function( id, kill ) {

                                // template node
                                var dom;

                                // get node by #ID
                                if( typeof id === 'string' )
                                        dom = document.getElementById( id );
                                // get node by DOM-Element
                                else if( id.nodeValue && id.nodeType )
                                        dom = id;

                                // break if no valid node given
                                if( !dom )
                                        return;

                                // traverse template node
                                // IMPORTANT - give a cloned node
                                var tmplID = traverseDOM( dom.cloneNode(true) );

                                // delete template source after traversing
                                if( kill )
                                        dom.parentNode.removeChild( dom );

                                // return ID of template
                                return tmplID;

                        },

                        // delete existing template
                        killTemplate: function( id ) {

                                if( templates[id] )
                                        delete templates[id];

                                return this;
                        },

                        // render existing template
                        render: function( id, data ) {

                                if( !templates[id] ) return;

                                var tmpl = templates[id],
                                        tmpl_shortcuts = tmpl.shortcuts,
                                        tmpl_keys = tmpl.keys,
                                        fragment = document.createDocumentFragment(),
                                        obj,
                                        shortcut_key,
                                        shortcut_dom,
                                        shortcut_obj,
                                        key,
                                        i, l=data.length,
                                        j, n = tmpl_keys.length;

                                for( i=0; i<l; i++ ) {
                                        obj = data[i];

                                        for( j=0; j<n; j++ ) {

                                                shortcut_key = tmpl_keys[j];
                                                shortcut_dom = tmpl_shortcuts[ shortcut_key ];
                                                shortcut_obj = obj[ shortcut_key ];

                                                for( key in shortcut_obj ) {

                                                        if( key == 'text' )
                                                                shortcut_dom[_innerText] = shortcut_obj[key];
                                                        else if( typeof shortcut_dom[key] !== 'undefined' )
                                                                shortcut_dom[key] = shortcut_obj[key];
                                                        else
                                                                shortcut_dom.setAttribute( key, shortcut_obj[key] );

                                                }

                                        }

                                        fragment.appendChild( tmpl.dom.cloneNode(true) );
                                }

                                return fragment;
                        }

                };
        }();

        window.templateJS = window.$t = templateJS;

}());
</script>

<li id="li-template" class="li" data-tmpl="li"><a class="click" target="_self" href="" data-tmpl="link"><img class="thumb" src="void:javascript(0)" data-tmpl="img"><div class="info"><h3 class="title" data-tmpl="title"></h3><p class="desc" data-tmpl="desc"></p><em class="misc" data-tmpl="misc"></em></div><em class="date" data-tmpl="date"></em><em class="price" data-tmpl="price"></em></a></li>

<ol id="li-box">
        </ol>
<script>
Benchmark.prototype.setup = function() {
    var box = document.getElementById('li-box'),
                t = $t.getTemplate("li-template");
   
    window.doTtemplate = doT.template('{{ for (var i = 0, l = it.data.length; i < l; i++) { }}<li class="item">{{= it.data[i] }}</li><li id="{{= it.data[i].li.id }}" class="li"><a class="click" target="_self" href="{{= it.data[i].link.href }}"><img class="thumb" src="{{= it.data[i].img.src }}"><div class="info"><h3 class="title">{{= it.data[i].title.text }}</h3><p class="desc">{{= it.data[i].desc.text }}</p><em class="misc">{{= it.data[i].misc.text }}</em></div><em class="date">{{= it.data[i].date.text }}</em><em class="price">{{= it.data[i].price.text }}</em></a></li>{{ } }}');
   
    var shortcut_list = {
        data: [
                {
                        li: { id:1 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 1" },
                        desc: { text:"Beschreibung 1" },
                        misc: { text:"Canton/Kategorie 1" },
                        date: { text:"Datum 1" },
                        price: { text:"Preis 1" }
                },
                {
                        li: { id:2 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 2" },
                        desc: { text:"Beschreibung 2" },
                        misc: { text:"Canton/Kategorie 2" },
                        date: { text:"Datum 2" },
                        price: { text:"Preis 2" }
                },
                {
                        li: { id:3 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 3" },
                        desc: { text:"Beschreibung 3" },
                        misc: { text:"Canton/Kategorie 3" },
                        date: { text:"Datum 3" },
                        price: { text:"Preis 3" }
                },
                {
                        li: { id:4 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 4" },
                        desc: { text:"Beschreibung 4" },
                        misc: { text:"Canton/Kategorie 4" },
                        date: { text:"Datum 4" },
                        price: { text:"Preis 4" }
                },
                {
                        li: { id:5 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 5" },
                        desc: { text:"Beschreibung 5" },
                        misc: { text:"Canton/Kategorie 5" },
                        date: { text:"Datum 5" },
                        price: { text:"Preis 5" }
                },
                {
                        li: { id:6 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 6" },
                        desc: { text:"Beschreibung 6" },
                        misc: { text:"Canton/Kategorie 6" },
                        date: { text:"Datum 6" },
                        price: { text:"Preis 6" }
                },
                {
                        li: { id:7 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 7" },
                        desc: { text:"Beschreibung 7" },
                        misc: { text:"Canton/Kategorie 7" },
                        date: { text:"Datum 7" },
                        price: { text:"Preis 7" }
                },
                {
                        li: { id:8 },
                        link: { href: "http://www.tutti.ch" },
                        img: { src: "http://lorempixel.com/200/100" },
                        title: { text:"Titel 8" },
                        desc: { text:"Beschreibung 8" },
                        misc: { text:"Canton/Kategorie 8" },
                        date: { text:"Datum 8" },
                        price: { text:"Preis 8" }
                }
        ]
    };
};

Benchmark.prototype.teardown = function() {
    box.innerHTML = "";
};
</script>

Preparation code output

    Test runner

    Warning! For accurate results, please disable Firebug before running the tests. (Why?)

    Java applet disabled.

    Testing in unknown unknown
    Test Ops/sec
    my own
    box.appendChild($t.render( t, shortcut_list.data ));
    pending…
    doT
    box.innerHTML = doTtemplate(shortcut_list);
    pending…
    compile my own
    var x = $t.getTemplate("li-template");
    pending…
    compile doT
    var y = doT.template('{{ for (var i = 0, l = it.data.length; i < l; i++) { }}<li class="item">{{= it.data[i] }}</li><li id="{{= it.data[i].li.id }}" class="li"><a class="click" target="_self" href="{{= it.data[i].link.href }}"><img class="thumb" src="{{= it.data[i].img.src }}"><div class="info"><h3 class="title">{{= it.data[i].title.text }}</h3><p class="desc">{{= it.data[i].desc.text }}</p><em class="misc">{{= it.data[i].misc.text }}</em></div><em class="date">{{= it.data[i].date.text }}</em><em class="price">{{= it.data[i].price.text }}</em></a></li>{{ } }}');
    pending…

    Compare results of other browsers

    Revisions

    You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

    0 comments

    Add a comment