Programmatic JavaScript Templating with buildr

Programmatic JavaScript Templating with buildrWe've all heard of Templating, heck I've written about templating. Templating is a great way to write HTML that is designed to be manipulated and inserted into a document via JavaScript, but it has a very annoying caveat: it's a huge pain to make templates external. They pretty much have to be inline, which doesn't allow you to maintain them separately. That's all about to change.

Code Your Templates with buildr

A relatively new tool has been released that takes the ideas from Markaby – HTML/XML markup expressed through programming code – and moves them over to JavaScript via a jQuery extension (not plugin) named buildr. Writing HTML using JavaScript code is no longer horrendously annoying.

To demonstrate how simple this is we'll need to create a bit of data to be adding into the HTML and do a tiny bit of other prep work. First, of course, make sure you're including jQuery and buildr. Now:

var chars = [
    'Luke Skywalker',
    'Han Solo',
    'Obi Wan Kenobi',
    'Chewey'
];

$container = $('<div>');

It's just a simple array of name strings and a jQuery object of a new div element. Now normally if we wanted to start creating more elements to put inside $container, we would use text(), html(), append(), or a combination of those. Some people might even start out by just creating a string with all of the HTML they need and putting it through the jQuery function. This, of course, is the type of thing we're trying to avoid. The former technique is difficult to interpret at a glance while the latter is hard to write and maintain because of writing multiline strings and escaping quotes.

With buildr, though, we get the best of both worlds: readability and simplicity. I'll show you how to write the below HTML using buildr:

<div>
    <h1 class="intro">Star Wars Characters</h1>
    <ul>
        <li>Luke Skywalker</li>
        <li>Han Solo</li>
        <li>Obi Wan Kenobi</li>
        <li>Chewey</li>
    </ul>
</div>

Go buildr Go!

$container.build( function (b) {
    b.h1( { class: 'intro' }, 'Star Wars Characters' );
    
    b.ul( 
        b.li('Luke Skywalker'),
        b.li('Han Solo'),
        b.li('Obi Wan Kenobi'),
        b.li('Chewey')
    );
});

Simply call build on the jQuery object that you wish to start adding children to and you'll be on your way. The buildr object is sent in as a parameter to the callback for build. With this buildr object, all you need to do is call a method named after any HTML element, and that element will be appended to $container. You'll notice that with b.h1 we sent in an object literal and a string. Any object literals sent into a buildr method will be used to create attributes for the element being built by that method. Also strings are used as text nodes that are appended to that element. Then, finally, we can make additional buildr method calls (all the b.li calls) as parameters to a buildr method (b.ul in this case), which will append those new elements to the outer element (the li elements will be appended to the ul rather than to $container).

This syntax brings the tag names to the front and makes the content of those elements more prominent. If you had used straight jQuery, the code would be overwhelmed with verbs like "append," making it difficult to interpret.

So far, I've shown you that you can send in objects, strings, and buildr objects. There's another thing you can send in: functions. By the way, you can send in any number of these parameters and you can send them in in any order (though the order of child elements is determined by the order of the parameters, with the exception of the object literal). Let's send in a function in order to clean up the code from above even more.

$container.build( function (b) {
    b.h1( { class: 'intro' }, 'Star Wars Characters' );
    
    b.ul( function () {
        $.each( chars, function ( i ) {
            b.li(chars[i]);
        });
    });
});

By sending in a function, we were able to iterate over the array of names instead of manually putting them in one-by-one. This is a little let readable than it was before, but the functionality is quite important.

There are few extra things you might want to note: buildr methods, such as h1, return a jQuery object extended by buildr. Also, because buildr objects are extended from jQuery, you can call any jQuery methods (such as event binding methods) on the buildr object.

buildr can be used in two ways for templating:

  1. After the DOM elements are built, you can call $container.html() to get the inner HTML as a string and have other templating libraries (such as underscore and Handlebars.js) handle the rest of the work.
  2. You can use buildr as the templating library itself, though you have to do the work of placing values into the elements on your own every time, rather than just calling a single method that does it for you.

Building Your Way to the End

Since using buildr can create templates using only JavaScript, it is much simpler to import templates onto your pages to use with your app. Heck, your Backbone.js views can use $script.js or RequireJS to get the templates they need themselves.

About the Author

Author: Joe Zim

Joe Zim

Joe Zimmerman has been doing web development ever since he found an HTML book on his dad's shelf when he was 12. Since then, JavaScript has grown in popularity and he has become passionate about it. He also loves to teach others though his blog and other popular blogs. When he's not writing code, he's spending time with his wife and children and leading them in God's Word.


  • Niki

    Nice post, but can you tell me what is the difference between jQuery plugin and extension?

    • http://www.joezimjs.com Joe Zimmerman

      Technically you could call it a plugin, but generally plugins just add extra functions to the jQuery object (whether directly to jQuery or to jQuery.fn), but this extension actually creates an entirely different API and then wraps jQuery to make it all work.

      • Niki

        Thanks for the answer. Now I understand the difference :)