Understanding JavaScript Micro-Templating

Zak Frisch
WDstack
Published in
10 min readNov 27, 2016

--

One of the more interesting things I’ve discovered recently was a post by

circa 2008. I can’t remember quite how I stumbled upon it. I think I was researching the merit of his written book “Secrets Of The JavaScript Ninja”(The Second Edition of which has just recently dropped and I highly recommend to any JS Developer. It’s great, honest.)

In any case I ran straight into John’s personal blog post dubbed JavaScript Micro Templating and it intrigued me because the code was succinct, though outdated, and it had quite a lot of power for how tiny it was. So me being me, I wondered, how does it work?

Well before jumping in to explain that, lets review some fundamental concepts.

What is Templating?

The first thing you may be wondering if you haven’t heard the term is what exactly is Templating, and secondly, why should you care?

Templating is something used within a variety of applications. It is provided generally as part of a greater library and can be found most notably within Mustache/Handlebars, lodash, KnockOut, Jade, Underscore, and as a plugin for JQuery.

It’s a technique that allows you to format a page and then feed it values to output so that the page doesn’t have to be recreated every time a value changes.

If that sounds confusing let me give you a quick example.

Lets say we had a website with two pages. A home page and an about page.

The home.html and about.html files are almost exactly the same. The only difference is the header of both pages. While the home page consists of:

...
<h1> This is the Home Page </h1>
...

the about page consists of:

...
<h1> This is the About Page </h1>
...

You can see how tedious this would be if we had dozens of pages. Now imagine if we had a single template that we could just pass the value of the page names into:

...
<h1> This is the <%pageName%> Page </h1>
...

You can see immediately why that would be much easier, and that’s Templating in a nutshell.

Creating Functions with Strings

Generally when we write JavaScript functions the resulting function declaration looks like this:

function demo() {
// function stuff here
}

You’re probably more than aware that “In JavaScript Functions are first-class Objects”. It’s a phrase that is spouted everywhere, and for good reason. It’s a very powerful idea that has worked to elevate JavaScript to where it is. We’re not going into the details of first-class objects here. All we care about is the fact that, in JavaScript, functions are objects.

This means that the above function can also be declared by calling its Constructor:

var demo = new Function();

Now lets imagine we have a function with parameters and instructions:

function demo(name, age) {
console.log('my name is ' + name);
console.log('my age is ' + age);
}

and now to convert it to Constructor syntax:

var demo = new Function(
"name,age",
"console.log('my name is ' + name);" +
"console.log('my age is ' + age);"
);

This makes it pretty easy to see that these two functions are the same, they are just declared differently. The important difference is that one has easily accessible strings that we should be able to manipulate.

Replacing Text Using RegEx

RegEx stands for Regular Expressions. Regular Expressions look for patterns in strings.

Using the .replace method of String objects we can use RegEx to look for a pattern in a string and then replace it.

RegEx is denoted by a pattern placed between a pair of backslashes.

An Example:

var myString = "hi my name is bob";
myString = myString.replace(/bob/, 'zak');
console.log(myString); // hi my name is zak

We can also use capture groups to pull information from the RegEx search and dump it into the replacement text. To create a capture group you simply wrap part of your RegEx pattern within parantheses. You then specify which capture group(you can have more than one) and insert it into the replaced text using the syntax: $NumberOfCaptureGroup

An Example with One Capture Group:

var myString = "hi my name is bob";
myString = myString.replace(/(bob)/, 'zak, but it used to be $1');
console.log(myString); // hi my name is zak, but it used to be bob.

An Example with Two Capture Groups:

var myString = "hi my name is bob";
myString = myString.replace(/(is) (bob)/, '$2 $1');
console.log(myString); // hi my name bob is

The final thing we’ll go over briefly is the most powerful part of RegEx. Using Tokens to define patterns instead of characters and using Flags to define how much of the string is affected.

Flags are descriptors of a RegEx’ scope. When a RegEx is denoted using backslashes a Flag is a single character placed after the second backslash that will apply a scope to the pattern.

The one we’ll be using today is g global match; find all matches rather than stopping after the first match.

  //without global flagmyString = "I am here. I am Zak.";
myString = myString.replace(/i/, "me");
console.log(myString); // me am here. I am Zak.
//with global flagmyString = "I am here. I am Zak.";
myString = myString.replace(/i/g, "me");
console.log(myString); // me am here. me am Zak.

The three Tokens we’ll be using are as follows:

. — the period. The period when placed in a RegEx pattern will match any character that is not a newline.

An Example:

var myString = "hi my name is \n Zak the Developer";
myString = myString.replace(/./g, '#');
console.log(myString);
//##############
//#################

The above will rewrite all characters(including spacing) with the “#” symbol. It will print out two lines to the console because the period Token does not affect new lines.

+ — the plus sign. The plus sign when placed in a RegEx pattern will look for repetition of the previous character or Token. It will continue matching the previous character or Token until a match is no longer able to be made.

var myString = "hi my name is Zak the Developer";
myString = myString.replace(/hi(.+)Zak/g, 'I am Bob');
console.log(myString); // I am Bob the Developer

The above finds the word “hi” and then captures every character afterwards until it runs into the name “Zak”. This pattern is useful when you are trying to find a value between two characters or strings.

?— the Question Mark. This Token actually serves multiple different purposes within Regex, however, for the purposes of this article we only care about one. The ability to make a Greedy pattern into a Lazy pattern.

What does that mean?

This question was asked in an SO post awhile back titled What do lazy and greedy mean in the context of regular expressions and there was a simple explanation given by user slebetman:

Greedy means match longest possible string.

Lazy means match shortest possible string.

For example, the greedy h.+l matches 'hell' in 'hello' but the lazy h.+?l matches 'hel'

RegEx is quite a bit more complicated than what we’ve gone over. RegEx isn’t the subject of this article however, and we won’t go any deeper into it from here. I have left a link in the resources section for those looking for more information.

It is only important for us to understand that we can find patterns using characters, Tokens, and flags — replace those patterns, and inject captured groups into a replaced string.

Creating Templating Functionality

We’ve gone over three main concepts and have a grasp of the following:

  • We know what Templating is.
  • We know that Functions can be declared using a Constructor that accepts strings.
  • We know that RegEx can be used to find, replace, and inject captured groups into strings.

How do we put this all together?

First we need to agree on Template Identifiers — what we’re going to use to determine a Template Variable and Template Loop from standard text. Keeping in line with the original code found in John Resig’s blog post lets use this:

<%= TemplateVariable %>     // this is using the identifier <%=
<% for loop %> // this is using the identifier <%

Next, of course, we need to design a Template:

<h1> Welcome to the <%= page.title %> Page</h1><h3> Please Visit our Friends: </h3><br>
<ul>
<% for (var i=0; i < page.links.length; i++) { %>
<li><%= page.links[i] %></li>
<% } %>
</ul>

This template broadcasts our Page Title and then uses a for loop to print out page links to our friends. It is standard fare to use an object to render the Template Variables and we won’t deviate from this tradition.

You might be wondering, how do we embed this into our html document? Much like CSS’ Flash of Unstyled Content(a literal flash of color or styling while the page renders), we don’t want our page to flash with our unformatted Template. That would look awful as well as disengage the user.

To avoid this we can simply use a script tag, with the caveat being that we set it’s type attribute to text/template:

<script type="text/template"></script>

This doesn’t innately do anything except that when the browser comes across this tag it will simply ignore everything contained within it. This is useful when it comes to Templating because we don’t want to render our Template immediately, but we do want the Template itself stored and easily accessible from JavaScript.

Our Template:

<script type="text/template" id="ourTemplate">
<h1> Welcome to the <%= page.title %> Page</h1>
<h3> Please Visit our Friends: </h3><br>
<ul>
<% for (var i=0; i < page.links.length; i++) { %>
<li><%= page.links[i] %></li>
<% } %>
</ul>
</script>

Accessing our Template and grabbing its content is pretty straight-forward:

var template = document.getElementById('ourTemplate');
var templateText = template.textContent;

Finally we need to develop the actual function that will take the Template, fill out the missing values, and render it to the page.

Developing the Templating Function

We’re about to design and implement our Templating function — what steps do we want it to take?

  1. Receive the Template Input as a String in our Templater Function.
  2. Create a Render Function using the Function Constructor.
  3. Parse the Template Input and Insert it into the Render function.
  4. Return the Render Function
  5. Pass our data into the Render Function and output it somewhere.

I realize the steps may seem tricky at first glance but as we walk through it will make quite a bit more sense.

Receive the Template Input as a String:

var template = document.getElementById('ourTemplate');
var templateText = template.textContent;
Templater(templateText);
function Templater(templateText) { //do other stuff here}

Create a Render Function using the Function Constructor:

var template = document.getElementById('ourTemplate');
var templateText = template.textContent;
var render = Templater(templateText);
function Templater(templateText) { return new Function();}

Parse the Template Input and Insert it into the Render function/
Return the Render Function

var template = document.getElementById('ourTemplate');
var templateText = template.textContent;
var render = Templater(templateText);
function Templater(templateText) {
return new Function(
"page",
"var output=" +
JSON.stringify(templateText)
.replace(/<%=(.+?)%>/g, '"+($1)+"')
.replace(/<%(.+?)%>/g, '";$1\noutput+="') +
";return output;"
);
}

The above looks to be the trickiest part of our code, but looks can be deceiving. It’s really just us replacing small sections of our string. One thing may stand out to you as something we haven’t touched on before:

JSON.stringify — As the name suggests the JSON.stringify method is typically used to convert JSON data to readable strings. This method is, in actuality, not limited to JSON data. In the above code we are using this functionality to make sure that we process our Template text as just a string. This allows our replace methods to work flawlessly.

The replace methods may seem unusual to you as well. Let’s go over them separately to make sure you understand why it’s actually not too complicated. Here’s what the first replace method is doing:

.replace(/<%=(.+?)%>/g, '"+($1)+"')
// this matches: <%= Page.title %>
// it captures: Page.title
// it replaces with: " + ( Page.title ) + "

When this replace is run within our Constructor Function the concatenated code looks like this:

var output="\n<h1> Welcome to the "+( page.title )+" Page</h1>\n<h3> Please Visit our Friends: </h3><br>\n<ul>  \n";

Here’s what the second replace method is doing:

.replace(/<%(.+?)%>/g, '";$1\noutput+="')
//this matches: <% for(var i=0;i<Page.links.length;i++) { %>
//it captures: for(var i=0;i<Page.links.length;i++) {
//this also matches: <% } %>
//it captures: }

When this replace is run within our Constructor Function the concatenated code looks like this:

for (var i=0; i < page.links.length; i++) { 
output+=" \n<li>"+( page.links[i] )+"</li> \n";
}

And if we look at the entirety of the Constructed Function(spaced a bit for readability):

function(page) {
var output="\n<h1> Welcome to the "+( page.title )+" Page</h1>\n<h3> Please Visit our Friends: </h3><br>\n<ul> \n";
for (var i=0; i < page.links.length; i++) {
output+=" \n<li>"+( page.links[i] )+"</li> \n";
}
output+=" \n</ul>\n";
return output;
})

Or as it will be formatted for return:

var output ="<h1> Welcome to the " + page.title + " Page </h1>
<h3>Please Visit our Friends: </h3>
<br>
<ul>"+
for (var i=0; i < page.links.length;i++) {
"<li>" + page.links[i] + "</li>" +
}
"</ul>"
return output;

As you can see it is really just a cluttered piece of code, but no more complicated than concatenating a few strings together.

The last thing we need to provide to the Template are the Template Variables(title and links) so that they can be rendered. To do this we simply create an object with the properties we will use in the Template:

var Page = {
title: "Home",
links: ['Google','CSS-Tricks','Codrops']
}

And append it to our current code:

var template = document.getElementById(‘ourTemplate’);
var templateText = template.textContent;
var render = Templater(templateText);
function Templater(templateText) {
return new Function(
“page”,
“var output=” +
JSON.stringify(templateText)
.replace(/<%=(.+?)%>/g, ‘“+($1)+”’)
.replace(/<%(.+?)%>/g, ‘“;$1\noutput+=”’) +
“;return output;”
);
}
var Page = {
title: “Home”,
links: [‘Google’,’CSS-Tricks’,’Codrops’]
}

Finally, we reach our last step. All that’s left to do is to tell the code where to render, call the render function, and pass in the page object with our Template Variables.

var template = document.getElementById('ourTemplate');
var templateText = template.textContent;
var render = Templater(templateText);
function Templater(templateText) {
return new Function(
"page",
"var output=" +
JSON.stringify(templateText)
.replace(/<%=(.+?)%>/g, '"+($1)+"')
.replace(/<%(.+?)%>/g, '";$1\noutput+="') +
";return output;"
);
}
var Page = {
title: "Home",
links: ['Google','CSS-Tricks','Codrops']
}
document.body.innerHTML = render(Page);
The Final Result

That’s all there is to it! Hopefully this has helped you understand the concept of Templating and how to implement a quick and concise version on the fly!

If you have any interest in pursuing the topic further please check out the resources below!

--

--