Introduction

This proposal describes how support for templates can be added to the jQuery core library. In particular, this proposal describes a new jQuery method – named render() – that enables you to render a single JavaScript object or array of JavaScript objects by using a fragment of HTML as a template.

The goal of this proposal is to enable plug-in developers to take advantage of a standard method for declaring and rendering templates. Having a standard method for declaring and rendering templates benefits everyone:

Plug-in developers can build rich, database-driven plug-ins such as DataGrid plug-ins. Plug-in developers can build on top of the support for templates in jQuery core and avoid re-implementing the code for rendering templates each time they need to develop a new plug-in.

Plug-in users can take advantage of a standard syntax for declaring templates. Plug-in users won’t need to learn a new way of creating a template each and every time they start using a new plug-in.

This proposal is divided into two main sections. The first section contains a brief survey of existing templating solutions. The second section contains our recommendation for how a standard method for declaring and rendering a template can be added to jQuery core.

Existing Templating Solutions

Indicating the popularity and need for a standard templating solution, there already are many existing JavaScript templating solutions. In this section, you are provided with a brief overview of four of the most popular and interesting ones. What are the existing templating solutions doing right? What features would make sense in jQuery core?

Micro-Templating

John Resig’s micro-templating engine is extremely small (only 2KB uncompressed). However, this tiny bit of code captures the core functionality needed to render a template.

Here’s an example of how you can use the micro-templating engine to display a single JavaScript product object:

Notice that the SCRIPT element’s type attribute has the value “text/html”. Web browsers ignore the contents of SCRIPT elements declared in this way and treat the contents of the SCRIPT element as a string.

Notice that the template includes expressions that represent the product name and price properties. The JavaScript formatPrice() method is called to format the product price. You can call any JavaScript function within a template.

The tmpl() method supports currying. If you call the tmpl() method without supplying data, then the method returns a JavaScript function that represents the parsed template. The returned method accepts a single data parameter.

In the code above, the template is parsed, and then the template method is called for each product to build a string. Finally, the string is assigned to the innerHTML of a DIV element named results.

Handlebars.js

The Handlebars templating engine can be used to render templates to jQuery objects using a `render` method through the handlebars plugin.

The plugin loads template files though AJAX and lazily precompiles and caches them. Support for partials is also provided.

While using this plugin you can still use the global `Handlebars` object so as to access all of handlebars.js’s features.

jTemplates

jTemplates is a feature rich templating engine that is implemented as a jQuery plug-in. jTemplates supports a number of advanced features including:

External Templates –You can load a template from an external file by supplying a URL. The external file can contain multiple templates.

External Data – You can load data from an external URL.

Live Refresh – You can update the content of a template on a regular interval automatically.

HTML Encoding – You can prevent JavaScript injection attacks by encoding characters such as < and >.

Includes – You can include one template in another template.

Debug Mode – You can cause the templating engine to break on errors and display error messages.

Here is sample code that demonstrates how you can display a list of products using jTemplates:

Notice that a jTemplate template can contain special commands such as #foreach, #for, and #if. As the call to formatPrice() illustrates, a template also can contain calls to arbitrary JavaScript functions.

By default, to prevent JavaScript injection attacks, jTemplate HTML encodes special characters included in the data passed to a template. For example, if the name of a product is ‚Äú<b>Laptop</b>‚Äù then the name is converted to ‚Äú&lt;b&gt;Laptop&lt;/b&gt;‚Äù.

jTemplates enables you to load both templates and data from an external URL. For example, the following code loads a template from a file named MyTemplates.htm and a set of data from a file named MyData.htm:

The contents contained in MyData.htm, of course, could be generated from a database dynamically.

PURE (Pure Unobtrusive Rendering Engine)

The PURE template engine is designed to enable developers to declare templates without using any special markup. There are two ways to use PURE: using the autoRender() method or using the render() method.

When you use the autoRender() method, PURE maps JSON property names to Cascading Style Sheet class names automatically. For example, here‚Äôs how you can write code to display a single product:

Notice that a set of directives is passed to the render() method with the item to be displayed. The directives map selectors to JSON property names. The first directive maps the SPAN element with an ID of name to the name property and the second directive maps the SPAN element with an ID of price to the price property. Here‚Äôs what the HTML looks like:

Notice that an ASP.NET Ajax template is just a DOM element. Because a template is just a DOM element, the template does not need to be wrapped in a SCRIPT or TEXTAREA tag or HTML comments. In this case, a new instance of the inner DIV element is created for each product.

Notice, furthermore, the $id() pseudo-variable. The $id() variable solves a problem related to templates and element IDs. If you add an element with an ID to a template, and the template is used with a collection of items, then you will end up with duplicate IDs. The $id() variable enables you to avoid this problem by generating unique IDs for each template instance.

The ASP.NET Ajax library also supports something called dynamic templates. For example, imagine that you have created one template for new products and one template for normal products that look like this:

The two templates are exactly the same except the new template includes an image that displays a New! icon.

You can create an itemRendering event handler that executes right before each template instance is displayed. Within the itemRendering event handler, you can specify the template that should be used to display a data item programmatically:

The code above uses one of two templates to display each product. If a product is new (it was created in the year 2010) then the product is displayed with the newTemplate template. Otherwise, the product is displayed with the normalTemplate template.

The ASP.NET Ajax library also supports something called dynamic placeholders. Dynamic placeholders enable you to display different items in different locations in a document. For example, you might want all of your new products to appear in a ‚Äúnew products‚Äù area of your document:

jQuery Smarty

The jQuery Smarty project supports the Smarty templating language (one of the most popular with PHP). This project supports a number of advanced features:

Pseudo variables ‚Äì You can use a special set of variables in a template such as the $smarty.foreach.theForeach.first variable that represents the current index of a template instance.

Ajax templates ‚Äì You can include template files automatically with AJAX by using {include file='include.tpl'}

OnChange: Auto Update Modifier ‚Äì By using the auto_update modifier, it will update the corresponding template block if any of the variables have changed. For example: {$title|default:"No title, so expect the rest to be blank as well..."|capitalize|auto_update}. or specify a selector to update: {$title|capitalize|auto_update:"title_span"}

OnChange: Event – You can hook into variable onchange events and add your custom handler – for instance if the $title variable updates, you can have a onchange handler to update document.title with the new value if the title variable changes.

Adding Templating Support to jQuery Core

This section contains a proposal for adding a standard method for declaring and rendering templates to jQuery core. The section contains a description of the templating API, code samples, and discussion points.

The API

jQuery.fn.render

Generates DOM elements by applying a template to a single data item or array of data items.

jQuery.templates

You can assign one or more compiled templates to the jQuery.templates settings object. This is useful when you want to provide a template with a semantic name so that you can easily use the same template multiple times in a document.

jQuery.tmplFn

Within a template instance, you can use the two built-in functions text() and html() to render a data item. You can extend the set of functions available within a template instance by assigning new functions to the jQuery.tmpFn object.

Example

The following code sample illustrates how you can create a custom even() function. The even() function returns true for alternating instances of a template. In the following sample, the even() function is used within the template to display alternating rows in bold.

Template Syntax

Inline expressions

Expressions can be inserted using the {%= ... %} syntax. This delimiter minimizes the chance of needing to escape the markup, while avoid collisions with existing server-side and client-side markup extensions (for example, <%= %> would conflict with ASP and ASP.NET).

Examples

The expression is JavaScript, so you may call any JavaScript function available or use more complex expressions. Note, however, that keeping the template as simple as possible is preferred, and the two callbacks described later help enable that.

HTML Injection:

By default, data items are not HTML encoded when they are rendered with a template. If you are displaying user submitted data in a template then a malicious user could perform a cross-site scripting (XSS) attack.

Notice the name of the first product in the following code. The first product contains an onclick handler that does something evil. When this data item is displayed, and someone clicks the product name, the JavaScript is executed.

In order to make it easier to HTML encode the data that you display in a template, and enable you to avoid these types of XSS attacks, there is a built-in function named text() that is available within a template instance. The text() function converts a data item into a text node. Here’s how you would use the text() function.

Context

You can take advantage of a special variable named $context within templates. This is the same object as described later in this proposal in the sections on the rendering and rendered callbacks. The $context variable has the following properties:

Property Name

Description

data

The array or object parameter passed to the render() or append() function.

dataItem

The current dataItem from the data parameter passed to template(). If that was an array, this is each item as the template is rendered for each item. Otherwise, it is the same object.

However, this is also put into a with() block, making the fields of each data item available directly. For example, if the data is [{name:"dave"},{name:"bob"}] then {%= name } may be used. Note however that it is invalid to refer to a field that may not exist. With the data [{name:"dave"},{firstname:"bob"}], the expression ‚Äòname‚Äô will be invalid for the 2nd entry.

index

The index of the current data item in the array given to template(). If an object was given, the index is 0.

options

The option parameter passed to template(). This provides a way of having extra parameters passed to the template.

Modifications that you make to the $context variable will persist in the template. For example, you can add calculated fields to the $context variable within the rendering() function:

You can use the $context.options variable to refer to any options passed to the render() or append() function. The following sample illustrates how you can display either the normal price or the sale price in a template depending on the value of a showSalePrice parameter passed to the append() function:

Notice how the $context.options.showSalePrice property is used in the template to display either the normal price or the sale price.

Rendering and Rendered Template Callbacks

You can take advantage of the options parameter that can be passed to either the render() or append() method to specify template callback functions. There are two callback functions:

rendering – The rendering function is called immediately before each template is rendered. It takes as a parameter the same $context object available within the template itself. Returning false from this callback prevents rendering of the item.

rendered – The rendered function is called immediately after each template is rendered, but before the resulting DOM element has been added to the document. It also takes as a parameter the same $context object available within the template itself.

Several scenarios for using the rendering and rendered callbacks are discussed in the following sections.

Performing Complex Calculations

Imagine that you want to calculate the tax for each product that you display in a template and you don’t want the application logic for calculating the tax to appear in the template itself. In that case, you can perform the tax calculation within the rendering function like this:

Canceling Template Rendering

You also can take advantage of the rendering callback to cancel the rendering of a particular template instance. The following code sample illustrates how you can render only those produces that are not marked as deleted.

When the rendering function returns the value false, the template is not rendered. In the code sample above, the template is not rendered when a product has been marked as deleted.

Nested Templates

By taking advantage of the rendered callback, you can created nested templates. For example, the following code illustrates how you can display a list of contacts. Each contact has one or more phone numbers. The contactTemplate is used to display the list of contacts. The phoneTemplate is used to display each phone number.

Creating Plug-ins

Imagine that you want to use a plug-in in a template — such as the jQuery UI Datepicker. Imagine, futhermore, that you want the plug-in to display the value of a data item. For example, you want the DatePicker to default to displaying a date property of the data item. In that case, you need the rendered() callback to create the plug-in and initialize the plug-in with the value of the data item property.

For example, the following code displays a list of contacts. The rendered() callback is used to create a jQuery UI Datepicker and associate the Datepicker with an input element in the template. The DatePicker defaults to the contact’s birthdate.

Expressionless Templates

If you don’t want any expressions such as {%= ... %} to appear in a template then you can use the rendered callback to programmatically assign values to elements in a template instance. For example, the following template displays a list of products. Notice that the template contains only HTML markup and no expressions.

Discussion

These are points raised for discussion in the forums.

HTML Encoding

In the current proposal, data items are not HTML encoded by default. That means that unless you take additional action, documents that use a template to display user submitted date will be open to Cross-Site Scripting (XSS) attacks. In an earlier proposal, we discussed creating a special delimiter for HTML encoding content such as {%! productName %}. In the current proposal, we suggest using the text() template function instead like this {%= text(productName) %}.

Script versus Style Template Container

In the current proposal, we recommend wrapping templates in a <script id="template" type="text/html"></script> element. It has been suggested that a better option would be to use the <style id="template" type="text/html"></style> element instead because a Style element better represents the presentational nature of a template.

The disadvantage of a Style element is that it is not allowed within the Body of a document. If you cannot modify the contents of the Head element — for example, you are working within a Content Management System — then you could not create templates.

Technically, the current proposal is compatible with wrapping templates in either a Script or a Style element. So, you could use Style elements, and alternative values for the type, if you prefer.

Templates should be Real DOM Elements

A couple of people have recommended that templates be represented with real DOM elements similar to the way that templates are created in the ASP.NET Ajax Library. In other words, use standard DOM elements for a template and hide the template with display:none.

The main disadvantage of using real DOM elements for templates is that using real DOM elements results in bad side effects. For example, consider the following template:

In this case, a browser will attempt to load an image located at {%= imageUrl } which is not what you want to happen. There are many other examples like this in which an expression will result in unintended browser side effects or invalid HTML. For example, an <input> within a template may actually post a value if it is within a form. Another good example of this is as in <div id="{= foo %}"/>, where the id attribute contains invalid characters per the standard.

Revision History

2/27/2010 — Initial proposal published

3/02/2010 – Proposal updated in response to community feedback.

Renamed renderTemplate() to template()

Changed delimiters from {{ ... }} to [[ ... ]].

Added new [[! ... ]] delimiters for displaying unescaped HTML.

Removed context variables and methods such as $id(), $index, and writeHtml(), write()

Added rendering() function

Added templateSetup() method

3/09/2010 – Proposal updated in response to John Resig prototype and community feedback.

Comments

(3/16/2010) Wondering if “$.fn.render” is best for this. Concerned about opportunities for namespace conflicts in using this generic name in view of the vast depth and breadth of possible implementations, of which the rendering is but one step, typically the final one.

(3/16/2010) Agreed; “render” is not clear, particularly given the "$(subject).verb(‘#object’, object) order of the proposed implementation. It is more natural to interpret that as rendering the subject, where in reality the proposal would be rendering the objects. I also find the “rendering” and “rendered” callbacks to be unhelpfully named. May i suggest $.fn.fill, as a verb with a better grammatical fit. It is also shorter, less jargon-y and aptly describes what is happening (the subject is being filled with the object(s)). Likewise, “onFill” and “postFill” would be shorter, more explicit and less intimidating than “rendering” and “rendered”. —Nathan Bubna

(3/16/2010) Fixed a typo using old style delimiters

(3/17/2010) Having an issue with the chosed template syntax, django templating uses {% .. %} which could cause issues or the need to extra escaping.

(7/7/2010) Yes, both {{ … }} and {% … } are Django template syntax, and hence wouldn’t even make it to the browser w/out escaping. Suggest that either, a) there must be a list of valid template delimiters (user chooses between <, {%, etc.. ), or we use something SGML-like, eg: <jQt>...</jQt>. EDIT: OR we look more at the approach taken by PURE, which doesn’t introduce any new syntax at all.

(8/20/2010) If using the PURE-style templates (no new syntax), we might consider using data-template-element-name or some other similar attribute on the nodes within the template instead of classes. Also, regardless of template language style, we might consider looking for all elements with a data-template-name attribute and storing those in jQuery.templates automatically.

(9/8/2010) Please note: This proposal, presented in February 2010, does not correspond to the actual design of the current implementation at http://github.com/nje/jquery-tmpl. More relevant (though not very complete) documentation can be found here: http://wiki.github.com/nje/jquery-tmpl.
We are working on complete documentation on a separate site, which should soon become available publically. — Boris Moore