Experimenting With The jQuery Template Markup Language (JTML)

Posted April 27, 2010 at 10:16 AM by Ben Nadel

Tags: Javascript / DHTML

Yesterday, on the YayQuery podcast, the group talked briefly about the jQuery Template proposal. This is the proposal outlining how jQuery might eventually handle templating - the act of rendering output based on a given template and a set of data. I took a brief look at the current proposal and a bunch of the ideas just didn't look right to me (granted I only skimmed the proposal). In my own work, I've gone about templating in a many different ways using everything from node cloning, to textarea values, to HTML comments, to Script tags (my current favorite). While each of these worked in their own way, they all shared a huge limitation - they only worked with a shallow collection of name-value pairs.

 
 
 
 
 
 
 
 
 
 

I come from a ColdFusion background. Now, say what you will about ColdFusion and domain modelling but, ColdFusion is pretty much the ultimate in templating languages. I think the reason ColdFusion works so well is because the rendering tags (CFML) integrate so seamlessly with the markup that they are trying to produce. Because of this, one can easily loop over queries, arrays, and perform conditional rendering logic without any tricky or hard-to-read syntax.

Using ColdFusion as a bit of inspiration, I wanted to see if I could create my own jQuery template rendering language which I will refer to as the "jQuery Template Markup Language" or JTML. The idea behind JTML is that, as with ColdFusion, the JTML tags will integrate seamlessly with the HTML markup as part of a template definition. I started working on this last night, so currently there are only two supported tags:

  • <jtml:if test="condition">
  • <jtml:else [test=""]>
  • </jtml:if>

... and:

  • <jtml:loop index="" key="" collection="">...</jtml:loop>

In the JTML IF tag, for parsing reasons, I chose to use an XSLT-style Test attribute rather than leaving the condition inline. This way, you could use the greater-than symbol within the quotes without confusing the tag parser. The Else tag can also take an optional test condition. The JTML Loop tag can iterate over an array or an object (as the collection). The Index attribute holds the current value while the optional Key attribute holds the array-index or object-key.

If you want to refer to variables within the actual JTML tags, all you have to do is reference them like standard variables. However, if you want to refer to variables outside of the JTML tags, you can ${..} notation to render the value of the given variable. So, for example, to output a girl's name, you could do something like:

  • <span class="name">${girl.name}</span>

I am not sure if I am 100% happy with this direction, but from my preliminary experimentation, I do enjoy having the JTML in tag format. With that said, let's take a look at how this might be used. In the following demo, I am simply rendering a template based on a data object with several nested collections:

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>jQuery Template Markup Language (JTML) Testing</title>
  • <script type="text/javascript" src="jquery-1.4.2.js"></script>
  • <script type="text/javascript" src="jquery.jtml.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, initialize the script.
  • $(function(){
  •  
  • // Create a new template instance based on the script
  • // template tag.
  • var template = new JTMLTemplate(
  • $( "script.template" )
  • );
  •  
  • // Define the data values to be used to render the
  • // jQuery / html template.
  • var data = {
  • listName: "Girls",
  • items: [
  • {
  • name: "Sarah",
  • attributes: {
  • hair: "Brunette",
  • plump: true,
  • isSexy: true
  • }
  • },
  • {
  • name: "Tricia",
  • attributes: {
  • hair: "Brunette",
  • athletic: true,
  • isSexy: true
  • }
  • },
  • {
  • name: "Lisa"
  • }
  • ]
  • };
  •  
  •  
  • // Create an instance of the rendered template. This
  • // return HTML that we can then turn into a jQuery
  • // collection.
  • var list = $( template.render( data ) );
  •  
  • // Append the list to the page.
  • $( "#output" ).append( list );
  •  
  • // Log rendered template content.
  • console.log( list.html() );
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery Template Markup Language (JTML) Testing
  • </h1>
  •  
  • <div id="output">
  • <!-- To be populated later. -->
  • </div>
  •  
  •  
  • <!--
  • Define the template we are going to use to define the
  • output. Notice that this template expects the following
  • values:
  •  
  • - listName
  • - items
  • -->
  • <script type="text/jtml" class="template">
  •  
  • <div class="list">
  •  
  • <h3>
  • ${listName}
  • </h3>
  •  
  • <!-- Check to see if there are any list items. -->
  • <jtml:if test="(items.length > 0)">
  •  
  • <ul>
  • <!-- Loop over list items. -->
  • <jtml:loop
  • index="girl"
  • key="i"
  • collection="items">
  •  
  • <li id="girl_${i}">
  •  
  • ${girl.name}
  •  
  • <!-- Check for attributes. -->
  • <jtml:if test="('attributes' in girl)">
  •  
  • <ul>
  • <!-- Loop over attributes. -->
  • <jtml:loop
  • index="value"
  • key="attribute"
  • collection="girl.attributes">
  •  
  • <li>
  • ${attribute} : ${value}
  • </li>
  •  
  • </jtml:loop>
  • </ul>
  •  
  • <jtml:else>
  •  
  • <br />
  • <em>No attributes</em>.
  •  
  • </jtml:if>
  • </li>
  •  
  • </jtml:loop>
  • </ul>
  •  
  • <jtml:else>
  •  
  • <p>
  • <em>There are no list items at this time.</em>
  • </p>
  •  
  • </jtml:if>
  •  
  • </div>
  •  
  • </script>
  •  
  • </body>
  • </html>

As you can see, I am using a Script tag to define my JTML template. The content of the template consists of standard HTML markup seamlessly integrated with JTML tags. Right now, the JTML tags are name-spaced with the "jtml:" prefix; I am not sure if I will keep this in the future, but for now, this seemed like an easy way to differentiate the types of markup tags.

In the Javascript, you'll notice that I am defining a Data object with dually-nested collections; the data contains an array of items (girls) and each item may or may not contain a list of attributes. Using the JTML tags, I can easily loop over and render each girl as well as check for the existence of optional attributes. When I run the above code, I get the following page output:

 
 
 
 
 
 
jQuery Template Markup Language (JTML) Testing. 
 
 
 

As you can see, the JTML engine was able to render the jQuery template based on the complex and nested data object.

When I have some time, I'll go back and read the jQuery Template proposal more thoroughly; but for now, I really like playing around with this raw idea. I think having rendering tags (JTML) easily integrate with the HTML markup is key to creating an effective templating engine. Right now, I am not sure what other kinds of tags I'd like to implement in the jQuery Template Markup Language. If you have any suggestions, please let me know.




Reader Comments

Apr 27, 2010 at 10:27 AM // reply »
5 Comments

There's already a javascript XSL engine as a jQuery plugin, take a look, http://www.jongma.org/webtools/jquery/xslt/


Apr 27, 2010 at 10:31 AM // reply »
304 Comments

No mention of Spry at all? :) I know it doesn't get much respect, and I myself no longer use it, but it really deserves credit for promoting this idea I think. Nothing does "load crap and display on page" as easily or direct as Spry I think.


Apr 27, 2010 at 10:31 AM // reply »
10,640 Comments

@Ricardo,

I figured there was stuff out there already - but it's always more fun to re-invent the wheel, if for no other reason than to get the creative juices flowing.

Last night, when I started thinking about this, I thought about using XSLT; but, after a minute or two of research, it looked like creating XML objects was overly complex in the various browsers. Plus, while I love XML with an intensity, I have to say that I have never been too pleased with XSLT.

I thought that this approach, JTML, was a nice mix of tags without having to worry about XML parsing.


Apr 27, 2010 at 10:32 AM // reply »
10,640 Comments

@Raymond,

It's funny you mention that - when I was first thinking about this last night, I wanted to look up SPRY. I even went to the labs.adobe.com site; but I found it really hard to find an example of a template rendering style scenario.

If you can point me in the right direction, I'd love to take a look. I've literally never ran SPRY before, so I didn't even know where to start.


Apr 27, 2010 at 10:42 AM // reply »
304 Comments

Yeah they kinda hide it now, it's listed here:

http://labs.adobe.com/technologies/

in the right hand side. Here is the real home for it:

http://labs.adobe.com/technologies/spry/


Apr 27, 2010 at 10:42 AM // reply »
304 Comments

Also, props to you for building it 'just to see' - nothing wrong with that. ;)


Apr 27, 2010 at 10:44 AM // reply »
10,640 Comments

@Raymond,

Cool - I'll take a look-see at lunch. And sometimes, things are just nice to force yourself to think in a different way. Who knows where the inspiration will lead.

Plus, I got to give props to ColdFusion :)


Apr 27, 2010 at 11:18 AM // reply »
5 Comments

Yes, you're right, it's good to just try to think things through and start over fresh.
And BTW, using JSON instead of XML is definitely a breakthrough!


Apr 27, 2010 at 11:20 AM // reply »
10,640 Comments

@Ricardo,

Word up - JSON is our friend!


Apr 27, 2010 at 11:24 AM // reply »
3 Comments

This looks as though it builds upon http://www.alistapart.com/articles/javascript-mvc/


Apr 27, 2010 at 11:27 AM // reply »
10,640 Comments

@Andrew,

I am not exactly sure what you mean. Can you explain further?


Apr 27, 2010 at 11:29 AM // reply »
5 Comments

I assume this is using a RexExp ${key} replace methodology similar to the one found in corMVC? My worry with RexExp (especially client-side) is performance. The (usually) necessary live() bindings on top of that could really start eating up cycles. Then again, I can be performance obsessive to my own detriment so I may be worrying about issues that don't exist in practical application.

I'm going to try and integrate this into my current project (which itself is based upon a heavily bruised version of corMVC) and see how it goes.

Oh, I have to agree with you about XSLT. I just feels so bloody 'heavy'. That might be because I've never given it the time it deserves, but like SOAP you have to lug around soooo much verbage. <jtml:> looks tiny and elegant in comparison.


Apr 27, 2010 at 11:48 AM // reply »
3 Comments

@Ben Well you have a Model (your list of models *wink* *wink* *nudge* *nudge*) and you have a template (which is basically the view), and whatever would put the two together and render it within the template would be your Controller.

Snook on A List Apart does kind of the same thing what with his replacement of values contained in ${} blocks.

However you seem to have taken this a bit further by adding tags and such.

The only problem that I see with all of this is when the google SEO comes through to read your site, there's nothing but JSON there, so I don't quite understand how the spider is supposed to pick it up.

(Unless of course on your server side you detect that a spider is looking at your site, and send it something else)

I've actually been looking for a solution to that problem ever since I saw Snook's JS MVC solution on A List Apart.


Apr 27, 2010 at 11:59 AM // reply »
10,640 Comments

@Grant,

I tried to optimize for performance as much as I could. Yes, it does use RegExp to replace out the ${..}, but only the first time the template compiles. It actually compiles the template down to a (new Function()), replacing out the variable place holders with actual Javascript variables.

There's some per-execution compiling because it needs to create the function in the context of the data map, so it actually ends up running the pre-compiled function as a sub-function of an on-the-fly function that locally-scopes the mapped keys. It's a bit hacky, but it's the only way I could figure out how to allow the function to be able to reference the data keys without having to scope them.

I even considered running it inside its own iFrame, but couldn't get that to work.

Hopefully, I'll have some time tonight to actually post the underlying code to the Project page.

@Andrew,

Ah, yes yes, I see what you mean. Yeah, I think this is the basic idea behind a lot of the rendering engines; even in Groovy which uses $varName as a "GString" place holder.

Jon Snook is a pretty brilliant guy; I will have to give the article you mentioned a read - I am sure there is some goodness I can borrow from it.

As far as the SEO stuff, I assume if you're using templates, you've already made the call that this data is gonna be loaded remotely, which would hurt SEO. I think this kind of stuff has more use for AJAX-returned data than inline data (where it's probably easier to render the HTML on the server anyway).


Apr 27, 2010 at 1:09 PM // reply »
3 Comments

@Ben Yeah, what you said about the SEO makes sense.

I think if you read though the comments in Snook article you'll find alot of people saying what I said, and it looks to me like Snook might not have made the fact that it was only intended for rendering ajax retrieved json data (or alot of people didn't read it). Rendering the entire page that way would definitely botch up the SEO.

You might want to put that in your article and tell Snook to put it in his.

(Although Snook's isn't jQuery based)


Apr 27, 2010 at 4:34 PM // reply »
74 Comments

And you got to mention the YayQuery podcast which did a shout out to you yesterday, right? Well played, sir, well played. ;)

(plus, gotta love you calling plump sexy. Sarah and the rest of us curvy girls send you a big thanks :D)


Apr 27, 2010 at 10:04 PM // reply »
48 Comments

Very cool stuff! (I'm really just subscribing to get more updates on this conversation)


Apr 28, 2010 at 7:44 AM // reply »
10,640 Comments

Hey all, I put the source code for this in the projects page now. Hopefully, this should generate some good ideas / conversations.

http://www.bennadel.com/blog/1908-The-jQuery-Template-Markup-Language-JTML-Project.htm



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 8, 2012 at 11:09 AM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
Here's a warning about using fixed top or bottom menu bars that hasn't been mentioned. Browsers don't factor fixed bars into the page height for page up and page down, meaning you'll have anything f ... read »
Feb 8, 2012 at 10:32 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
@Andy, Ah, very cool. FW/1 really seems to be quite well-rounded these days! ... read »
Feb 8, 2012 at 9:52 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
Just wanted to let you know that version 2.0 of Sean Corfield's FW/1 supports routing. This allows you to build true RESTful APIs using ColdFusion. (search for "URL Routes" https://github ... read »
Feb 8, 2012 at 2:05 AM
Creating A Fixed-Length Queue In JavaScript Using Arrays
Cool site ... read »
Feb 7, 2012 at 5:00 PM
Ask Ben: Ending ColdFusion Session When User Closes Browser
We've used code that sets the cookies without the "expires" attribute in most of our applications to accommodate an "automatic logoff" (Let's face it, that's what we're really try ... read »
Feb 7, 2012 at 10:10 AM
Ask Ben: Building An AJAX, jQuery, And ColdFusion Powered Application
Hey Ben great post. Just like @Carl Steinhilber I am having the same trouble with the -'parseerror'. But I can only reach GET contacts-demo.cfm I have added secureJSON="no" ... read »
ang
Feb 7, 2012 at 4:46 AM
Using The Apple iPod Shuffle Without iTunes
i got the same error with munkey!! help please!! ... read »
Feb 7, 2012 at 2:48 AM
Ask Ben: Javascript String Replace Method
@Kadut, . is a special character you will need to escape it \. ... read »