RND - fast and simple JS template system

As web applications get more dynamic and complex, it's crucial to know what's the fastest way to render content. I have done some research and I am gladly sharing it with you. The things I cover:
  • HTML render benchmarking: test of basic DOM, AJS DOM, innerHTML and RND template.
  • RND template: Python inspired template system that's super simple and super fast.
  • Real world usage: How I gained 80x performance, while getting much cleaner code!

HTML render benchmarking

There are different ways of adding/manipulating HTML, those that I am going to test are following:

  • Basic DOM: Using document.createElement(...).
  • AJS DOM: From AmiJS. Example: AJS.DIV(...).
  • innerHTML: elm.innerHTML = ...
  • RND template: Template system that we will cover later on.

I have benchmarked in following browsers:

  • IE 6 (Windows)
  • FireFox 1.5.0.6 (Mac OS X)
  • Safari 2.0.4
  • Opera 9.0.0 (Mac OS X)

Basically we insert following HTML around 10000 times using the different methods:

<span class="test">
  babble, babble, bitch, bitch
  <div><img src="hej.png" /></div>
</span>

The results

A table over my benchmarks:
Rendering benchmarks

As you can see, using DOM is slow. While using innerHTML is the fastest method.

All around, it's around 3 times faster using innerHTML than DOM. Remember, this is for very basic HTML.

IE was tested on a 3GHz machine with 1 GB ram. The others were tested on a Powermac 2x2.0 GHz G5 with 2 GB ram.

Why not to use innerHTML

Using innerHTML approach can be dirty, mostly because HTML code is spread around. That's when I got the idea to a simple JS template engine. I named it RND and by the benchmarks you can see that it's almost as fast as using innerHTML!

RND JS template

Let me start off by showing the implementation, because it's simple and beautiful:

function RND(tmpl, ns) {
  var fn = function(w, g) {
    g = g.split("|");
    var cnt = ns[g[0]];
    for(var i=1; i < g.length; i++)
      cnt = eval(g[i])(cnt);
    return cnt || w;
  };
  return tmpl.replace(/%(([A-Za-z0-9_|.]))/g, fn);
}

An example of usage

This JS code:

var tmpl = '<a href="%(link)">%(value|parseInt)</a>';
var name_space = {'link': 'http://amix.dk', 'value': 5.5};
alert( RND(tmpl, name_space) );

Will alert:

<a href="http://amix.dk">5</a>

The syntax

In a string you use %(NAME) to denote the placement of placeholders. In the example you can see that placeholders are %(link) and %(value|parseInt). Notice the use of filter function parseInt, i.e. you can pass any functions as filters and you can combine filters.

To fill your template you call RND(tmpl_string, name_sapce), where

  • tmpl_string: A string that contains placeholders.
  • name_space: A JSON object that contains the values of placeholders.

The reasons why RND is good:

  • Good performance
  • HTML representation can be separated from the actual data
  • Templates can be reused

A more complex example

In the benchmarks I used following template:


tmpl = 'Time run: %(time_run)';
tmpl += 'Number of iterations: %(iterations)';
tmpl += 'Basic DOM: %(t_b_dom|getAvg|parseInt) ms';
tmpl += 'AJS DOM: %(t_ajs_dom|getAvg|parseInt) ms';
tmpl += 'innerHTML: %(t_inner|getAvg|parseInt) ms';
tmpl += 'RND template: %(t_rnd|getAvg|parseInt) ms';

Real world usage of RND

Skeletonz has a very nifty plugin syntax builder. The HTML is rendered from JSON and the HTML building was done using AmiJS DOM.

Before I changed the slowest method to use RND I took some benchmarks.

To render plugin syntax HTML using AmiJS DOM:

  • around 950 ms!

To render plugin syntax using RND:

  • around 12 ms!

That's around 80x improvement ~ the code is also much cleaner since HTML representation and actual code are separated.

I am suprised by this result, but happy that I found a solution :)

The tests for Skeletonz were done only in Firefox, using the same benchmarking suite as you'll find in the zip below.

Download

Update [13. Aug 2006]

Code · Code improvement · JavaScript 11. Aug 2006
© Amir Salihefendic. Powered by Skeletonz.