public
Last active

JavaScript Micro Templating

  • Download Gist
1-tmpl.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
// @see http://ejohn.org/blog/javascript-micro-templating/
(function(){
var cache = {};
 
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
 
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
 
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
 
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
 
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();
2-helloTmpl.html
HTML
1 2 3 4 5 6 7 8 9 10 11
<script type="text/html" id="helloTmpl">
<%
/*
We'll be nice and safe inside our template and use var to create
some pretty display values.
*/
var name = name.toUpperCase();
var age = age + 1;
%>
HELLO, <%= name %>. YOU WILL BE <%= age %> NEXT YEAR.
</script>
3-whereItAllGoesWrong.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
// Grab the template data from our <script> tag.
var template = document.getElementById('helloTmpl').innerHTML;
 
// Convert the template data a renderer function.
var renderer = tmpl(template);
 
// Objectify me.
var furf = {
name: 'furf',
age: 37
};
 
// Let's inspect furf.
 
console.dir(furf);
// {
// age: 37
// name: 'furf',
// }
 
// Looks good so far. Now let's render our template to the console.
 
console.log(renderer(furf));
// HELLO, FURF. YOU WILL BE 38 NEXT YEAR.
 
// Awesome! Looks like our template is rendering just fine. Now, let's
// check in on me and see how I'm doing.
 
console.dir(furf)
// {
// age: 38
// name: 'FURF',
// }
 
// GAK! Another year older and louder!
 
// It seems inside a with statement, even "var" will not protect your
// object when declaring a variable with a name that matches one of its
// properties.
 
// The fix? DON'T USE WITH(){}!
 
// Instead, wrap the renderer function with one that calls it in the
// context of the data object and use the this keyword to access its
// values.
 
// Minor inconvenience? Sure. The benefit of clarity, worth it.
4-modifiedTmpl.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
 
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
 
// Introduce the data as local variables using with(){}
"p.push('" +
 
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');return p.join('');");
 
function wrap (data) {
return fn.call(data);
}
 
// Provide some basic currying to the user
return data ? wrap( data ) : wrap;
};
5-modifiedHelloTmpl.html
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<script type="text/html" id="helloTmpl">
<%
/*
Now we can safely declare new variables inside our template.
 
(Though this is probably still bad form and a view of the
object should be passed in instead rather than the object
itself.)
*/
var name = this.name.toUpperCase();
var age = this.age + 1;
%>
HELLO, <%= name %>. YOU WILL BE <%= age %> NEXT YEAR.
</script>
6-modifiedOutput.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
console.dir(furf);
// {
// age: 37
// name: 'furf',
// }
 
console.log(renderer(furf));
// HELLO, FURF. YOU WILL BE 38 NEXT YEAR.
 
console.dir(furf)
// {
// age: 37
// name: 'furf',
// }
 
// Yay! I made it out unscathed! Still young and pretty.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.