Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Colleen Lanham and Demian Holmberg and Robert Dix and Paul Carney and Eric Pfleckl
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Colleen Lanham , Demian Holmberg@dah007 ) , Robert Dix@p_robert_dix ) , Paul Carney@perpetapaul ) , and Eric Pfleckl@epfleckl )

Creating jQuery Templates Plug-in Using Textarea Elements (Thanks Kurt Bonnet)

By Ben Nadel on

A while back, Kurt Bonnet introduced me to the idea of using HTML Textarea elements to store the markup for HTML templates for use within jQuery. I used to use ColdFusion as a way to generate my dynamic HTML, but lately, I have been getting more into the idea of passing back raw data from ColdFusion and then using client-side templates to create my new DOM elements. Something about this feels like a really nice separation of "Data" and "View" in a rich-client model. To make this process as easy as possible, I thought I'd create a jQuery plug-in that adds the method template() to Textarea elements. Here is a demo of this in action:


Here is the page that I am demoing in the above video:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">
  • <html>
  • <head>
  • <title>jQuery HTML Templates Using Textarea Elements</title>
  • <script type="text/javascript" src="jquery-1.2.6.min.js"></script>
  • <script type="text/javascript" src="jquery.template.js"></script>
  • <script type="text/javascript">
  • // Hook up the add button handler.
  • $(
  • function(){
  • // Get a refernece to the values text area.
  • var jValues = $( "#values" );
  • // Get a refernece to the template text area.
  • var jTemplate = $( "#template" );
  • // Keep track of the templates created.
  • var intCount = 0;
  • // Bind the click event.
  • $( "button" ).click(
  • function(){
  • // Get the new element from our jQuery
  • // template. When we do this, we are going
  • // to pass in some values that can be
  • // leveraged.
  • jElement = jTemplate.template(
  • eval( "(" + jValues.val() + ")" )
  • );
  • // Increment the count.
  • intCount++;
  • // Add the element to the page.
  • $( "body" ).append( jElement );
  • }
  • );
  • }
  • );
  • </script>
  • </head>
  • <body>
  • <h1>
  • jQuery HTML Templates Using Textarea Elements
  • </h1>
  • <form>
  • <p>
  • Please enter your template code in the
  • </p>
  • <textarea
  • id="values"
  • style="width: 500px ; height: 125px ;">
  • {
  • count: intCount,
  • name: "Naomi",
  • description: "Sexy"
  • }
  • </textarea>
  • <br />
  • <textarea
  • id="template"
  • style="width: 500px ; height: 125px ;">
  • <p id="template{count}">
  • <strong>{name}</strong> is <em>{description}</em>
  • I am template {count}.
  • </p>
  • </textarea>
  • <br />
  • <button type="button">Add Template</button>
  • </form>
  • </body>
  • </html>

As you can see in the above code, to convert the HTML code in the textarea into a new DOM element, we are simply calling template() on jQuery textarea (jQuery stack containing the textarea as the first element) and passing in the name-value pairs that will be used for the variables. This method returns a new jQuery stack containing the new DOM element. We are using a Textarea element to hold the template value because they are easy to code (the HTML does not need to be massaged to fit the textarea) and the browser won't try to interpret the code inside. Plus, we can easily hide textareas with a little CSS.

The jQuery plug-in that makes this possible is actually quite simple. It is not much more than a loop and a regular expression that swaps out the variable names with the given variable values:

  • // Define the jQuery Template plugin. This takes a textarea
  • // value and converts it into an jQuery DOM elements (outside
  • // of the current DOM) and returns it. It takes only one
  • // argument: the name-value pairs of the values to replace
  • // into the template.
  • jQuery.fn.template = function( objValues ){
  • // Get a reference to the current jQuery stack.
  • var jThis = this;
  • // Get the value of the textarea.
  • var strHTML = jThis.val();
  • // This will be our index variable for looping over the
  • // values that were passed in.
  • var strKey = "";
  • // Check to make sure we have a value string. If this is
  • // not the right kind of jQuery stack, the HTML string will
  • // be null.
  • if (strHTML){
  • // Now that we have the proper value, we have to
  • // replace in the mapped values. Loop over each
  • // value that was passed in.
  • for (strKey in objValues){
  • // Escape all the special values in the key so that
  • // it can be used in a regular expression.
  • strSafeKey = strKey.replace(
  • new RegExp(
  • "([\\[\\]\\.\\+\\*\\{\\}\\(\\)\\$\\?\\-])",
  • "gi"
  • ),
  • "\\$1"
  • );
  • // Replace the value.
  • strHTML = strHTML.replace(
  • new RegExp( "\\{" + strSafeKey + "\\}", "gi" ),
  • objValues[ strKey ]
  • );
  • }
  • // At this point, our HTML will have fully replaced
  • // values. Now, let's convert it into a jQuery DOM
  • // element and return it.
  • return( jQuery( strHTML ) );
  • } else {
  • // Return empty jQuery stack.
  • return( jQuery( [] ) );
  • }
  • }

So far, I am really liking this method. Thanks Kurt for your inspiration!

Reader Comments

Yeah, this is one area where I think Spry really shines. When it comes to 'Get remote crap and display it', I think Spry does it best. Thanks for blogging this - I could have used it for my last LighthousePro update. :)


Happy to share. I think there are still gonna be times where I want ColdFusion to build the interface and just return it; but, I am trying to get my data to be more reusable and let the client handle the display.


I'm glad those links helped out. Nice work on the jQuery plug-in, I love how simple and powerful it is. I look forward to making use of it :) Nice video too; man you went all out for this post!


Thanks :) I am finding that the video really ties things together nicely, especially when the content of the post is action-based.

Thanks for directing me to this post I think i'll be able to make use of this plugin. In a more practical example the text areas would be hidden right? Therefore hiding your template on the page until the remote data is called and appended to the page? Thanks again for all your posts they are always so practical and clear.

There is one question that I was curious about. How would it be possible to emulate the cfoutput query using the group attribute by looping through the returned json. Ideally the ProductCatName and ProductName variables would be in h4 h5 tags respectively and the list of serial numbers would be in a table. Any thoughts on how to approach generating a template to emulate this type of grouping and display.

<cfoutput query="myQuery" group="ProductCatID">
<cfoutput group="ProductID">

Thanks in advance for any thoughts on how to accomplish this.


First off, I wouldn't return a query via JSON. Something about queries just don't feel right to me in an AJAX world. I prefer arrays of structures. And, once you are in this model, you can return a JSON object that breaks its data up in the appropriate way: master array in which each index is "Section" of the content complete with title and its own nested array of products.

Not the only way, but it would be really easy to loop over an array of structs than a query.

I have found using Spry on the client side to handle a query via JSON works very well. I can even strip out 'null' values to reduce the size of the response; Spry handles it just fine. Small snippet here (I have extra functions for application security checks, caching, and custom JSON prefixing; done in CF8)

<!--- Vendor Contact --->
<!--- remote function --->
<cffunction name="DFF8D96F_6704_4CE2_98B3_94D077362085" access="remote" returntype="string" output="no" returnformat="plain">
<cfargument name="fooDog" type="numeric" required="yes">

var checkUser = Request.cfc_Data.CheckUser(VendorID=Arguments.fooDog);
var qry = getVendorContactByID(checkUser.VendorID);
<cfinclude template="no_cache.cfm">
<cfreturn "//" & Session.UserInfo.PageKey & Replace(SerializeJSON(qry, true), 'null', '', 'ALL')>

<!--- private function --->
<cffunction name="getVendorContactByID" access="private" returntype="query" output="no">
<cfargument name="VendorID" required="yes" type="numeric">
<cfquery name="q_qResult" datasource="#Request.DBSource#" username="#Request.DBUser#" password="#Request.DBPass#">
EXEC usp_dm_GetVendorContactByID
@VendorID = #VendorID#
<cfreturn qResult>

It returns:

//4129655EF4D302F8940D7785A67024BEA53060D2ACFCF32129AFB9D28789F1D21D580CDC3B2D734088474E63E5811359DC1029D6C77F81002ACA91527ED89499{"ROWCOUNT":1,"COLUMNS":["F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","F13","F14","F15","F16","F17","F18","F19","F20","F21","LMS"],"DATA":{"F1":[1],"F2":[0],"F3":[0],"F4":[0],"F5":[0],"F6":[0],"F7":[0],"F8":[0],"F9":[0],"F10":[0],"F11":[0],"F12":[1],"F13":["FooBar"],"F14":["Peter"],"F15":["Senior Programmer"],"F16":[],"F17":[],"F18":[],"F19":[],"F20":[],"F21":[529],"LMS":["2009-02-27 23:39:52.687"]}}

Then in Spry:

var dsCurrentData = new Spry.Data.JSONDataSet("cfc/ops_vendor.cfc?method=DFF8D96F_6704_4CE2_98B3_94D077362085&fooDog={dsVendorList::F1}", optJSON);

My JSON options:

var optJSON = new Object();
optJSON.path = "DATA";
optJSON.pathIsObjectOfArrays = true;
optJSON.preparseFunc = JSONPreparse;

Spry handles the layout as documented.


Spry is a very nice and flexible AJAX alternative to using Flex. I have products used by clients with older machines and browsers, so Spry makes it possible to work with them until they update to a Flash-based interface. It's easily modified, too, when you need your own tweaks.

Another +1 for Spry. I'm definitely a jQuery FanBoy now, but Spry was my 'gateway' drug into AJAX. Also, if you are a DreamweaverCS4 user, there are -very- tight integrations between Spry and your code.

Quick note - I used this idea to build a similar system. You just have to watch it if you have textareas as part of your content. It breaks the template. In my case, i was able to switch it to a DIV instead but I wondered if there is a better solution.


I've been enjoying Script-tag-based html data lately; it will, of course, suffere from some nested Script tag issues, but that is less likely than standard HTML elements.