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

Posted November 10, 2008 at 9:06 AM

Tags: Javascript / DHTML

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:

 Launch code in new window » Download code as text file »

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <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:

 Launch code in new window » Download code as text file »

  • // 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!

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page





Reader Comments

Nov 10, 2008 at 12:30 PM // reply »
204 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. :)


Nov 10, 2008 at 12:51 PM // reply »
6,371 Comments

@Ray,

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.


Kurt Bonnet
Nov 10, 2008 at 1:09 PM // reply »
22 Comments

@Ben,

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!


Nov 10, 2008 at 1:11 PM // reply »
6,371 Comments

@Kurt,

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


Tim
Feb 18, 2009 at 10:21 AM // reply »
6 Comments

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.


Feb 18, 2009 at 10:23 AM // reply »
6,371 Comments

@Tim,

Correct, in reality the textareas would be hidden.


Tim
Feb 18, 2009 at 10:32 AM // reply »
6 Comments

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">
#ProductCatName#
<cfoutput group="ProductID">
#ProductName#
<cfoutput>
#SerialNum#
</cfoutput>
</cfouput>
</cfoutput>

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


Feb 18, 2009 at 10:46 AM // reply »
6,371 Comments

@Tim,

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.


Peter
Mar 25, 2009 at 4:40 PM // reply »
2 Comments

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">

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

<!--- 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#
</cfquery>
<cfreturn qResult>
</cffunction>

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.


Mar 25, 2009 at 4:42 PM // reply »
6,371 Comments

@Peter,

I have heard really nice things about SPRY, but have not had the chance to use it myself.


Peter
Mar 25, 2009 at 4:48 PM // reply »
2 Comments

@Ben,

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.


Mar 25, 2009 at 4:51 PM // reply »
204 Comments

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.


Oct 8, 2009 at 12:44 PM // reply »
10 Comments

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.


Oct 31, 2009 at 5:40 PM // reply »
6,371 Comments

@Aaron,

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.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 7, 2009 at 5:53 PM
Ask Ben: Javascript String Replace Method
You can find here an advanced function that prepared with javascript replace function. This can make the first letters of words, sentences, lines and whatever you define automatically: http://www.m ... read »
Andrew Neely
Nov 7, 2009 at 4:56 PM
A Moment That Touched Me - The Fountainhead
Ben, Glad you enjoyed the podcast. Yeah, the Tank Riot guys can get really chatty during the episodes, but that's part of the charm of it for me. They've covered everything from Nichola Tesla to Cha ... read »
Nov 7, 2009 at 4:43 PM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
Is it possible to make some more MenĂ¼`s ? ... read »
Jill
Nov 7, 2009 at 11:40 AM
How To Unformat Your Code (Like A Pro)
Derek, I think you might be right - sweet! Thanks for the link :) ... read »
Nov 7, 2009 at 11:25 AM
How To Unformat Your Code (Like A Pro)
I think it would be way easier to just use this http://www.logichammer.com/html-formatter/ He just released v3 and it rocks. ... read »
Jill
Nov 7, 2009 at 7:58 AM
How To Unformat Your Code (Like A Pro)
LMAO - this was pretty funny! I have to admit - I also love to reformat code so I can read it. My boss used to tell me to leave my OCD at home. Now I don't feel so bad after reading everyone else' ... read »
Nov 6, 2009 at 10:10 PM
How To Unformat Your Code (Like A Pro)
The timing of this post is just uncanny. I spent the last 15-20 minutes manually un-formatting my "Ben Nadel" style code within a CFC of mine. I was really digging the readability a few weeks ago, bu ... read »
Roe
Nov 6, 2009 at 5:11 PM
Passing Arrays By Reference In ColdFusion - SWEEET!
ArraySort also reorders the results of these java obj's ... read »