Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
jQuery Template Based Markup Generation
JavaScript Makefile HTML CSS

README.md

jQuery Markup

(jQuery Template Based Markup Generation)

Abstract

jQuery Markup is a jQuery plugin for assembling, pre-compiling, expanding and then DOM-injecting HTML markup code fragments based on plain HTML or an arbitrary template language.

Motivation

When developing a Single-Page Application (SPA), JavaScript code is used to render the entire UI in the browser on-the-fly. The UI is still based on HTML markup and CSS stylesheets. While the CSS stylesheets are (as their name implies) inherently cascading and are applied by the browser once the HTML markup comes into place, the HTML markup has to be manually rendered via JavaScript into the DOM tree. For this various so-called HTML template languages exist. They allow you to describe the markup in more concise and partially dynamic language and render the static plain HTML markup on-the-fly. But a handy solution is needed to on-the-fly pick up particular templates, render them and inject them into the DOM. This is where jQuery.Markup comes into the play.

Solution

First, the HTML markup is linked to the SPA with <link> tags similar to CSS stylesheets, but instead of a rel attribute with value stylesheet, the value markup is used. The attribute type is set to text/x-markup-xxx to tell jQuery.Markup about the default template language (xxx):

<!DOCTYPE html>
<html>
    <head>
        [...]
        <script src="app.js" type="text/javascript"></script>
        <link href="app.css" rel="stylesheet" type="text/css"/>
        <link href="app.html" rel="markup" type="text/x-markup-handlebars"/>
        [...]
    </head>
   [...]
</html>

Second, a markup template is a regular HTML/XHTML file, but with one or more <markup> tags at the top-level (and optionally also at a deeper nested level):

<!-- container markup template -->
<markup id="hello">
   <div class="hello">
       Welcome:<br/>
       <!-- embedded message markup template -->
       <markup id="message">
           <div class="message row-{{k}}">{{i}}. {{message}}</div>
       </markup>
   </div>
</markup>

<!-- some other template -->
<markup id="foo">
   <div class="foo">{{foo}}</div>
</markup>

Third, in your application give jQuery.Markup a chance to load the markup template files, parse and compile the contained <markup> sections and tell you once it finished this step:

$(document).ready(function () {
    $.markup.load(function () {
        /* ...start your SPA code here... */
    });
});

Finally, render some markup into the DOM through the pre-compiled and stored markup templates:

/*  render a "hello" container markup  */
var h = $("body").markup("hello");

/*  insert ten message markups  */
for (var i = 0; i < 10; i++) {
    $(h).markup("hello/message", {
        i: i, k: i % 2,
        message: "Hello World"
    });
}

In this example, the resulting DOM fragment will be:

<body>
   [...]
   <div class="hello">
       Welcome:<br/>
       <div class="message row-0">0. Hello World</div>
       <div class="message row-1">1. Hello World</div>
       <div class="message row-0">2. Hello World</div>
       <div class="message row-1">3. Hello World</div>
       <div class="message row-0">4. Hello World</div>
       <div class="message row-1">5. Hello World</div>
       <div class="message row-0">6. Hello World</div>
       <div class="message row-1">7. Hello World</div>
       <div class="message row-0">8. Hello World</div>
       <div class="message row-1">9. Hello World</div>
   </div>
   [...]
</body>

API

The Application Programming Interface (API) of jQuery.Markup is (in abbreviated TypeScript definition syntax):

/*  global version number  */
$.markup.version: String;

/*  global debug level  */
$.markup.debug: Number;

/*  register a template engine  */
$.markup.register({
    id: String;
    name: String;
    url: String;
    available: () => boolean;
    compile: (txt: String) => (data?: Object) => String;
}): void;

/*  compile and store a template fragment  */
$.markup.compile(type: String, id: String, txt: String): void;

/*  parse a manually loaded template file  */
$.markup.parse(txt: String, type?: String): void;

/*  manually queue the loading of particular template file  */
$.markup.queue(url: String, type?: String): void;

/*  load all queued template files  */
$.markup.load(onDone: () => void): void;

/*  render a particular markup template (into unparsed/textual format)  */
$.markup.render(id: String, data: Object): String;

/*  render a particular markup template (DOM unattached)  */
$.markup(id: String, data?: Object): jQuery;

/*  render a particular markup template (DOM attached)  */
$([...]).markup(id: String, data?: Object): jQuery;

In practice you usually need only the $.markup.load() function for loading all your markup templates once on startup and the $.markup() function for rendering a particular markup template into a DOM fragment. All other functions are just the exposed lower-level functionality and are usually directly used in rare situations only.

Templates

The template files can contain arbitrary HTML/XHTML markup, just surrounded with <markup> tags. Those tags have the following syntax:

<markup id="ID" [type="TYPE"] [include="true|false"] [wrap="true|false"] [trim="true|false"]>
    [...]
</markup>

The id attribute has to be globally unique for top-level tags and locally unique for nested tags. The type attribute can be any registered name of a template language (see below for pre-registered ones). The include attribute can be set to true for nested tags in order to allow their content to be included into the parent tag (by default nested tags are treated as they would be located at the top-level and are just nested for logical reasons only). The wrap attribute can be set to true for markups where an outer wrapping <div> element should be generated with a class derived from the effective markup id (a minus-seperated path of all markup ids, starting at the top-level). The trim attribute can be set to true for markups where whitespaces around HTML tags should be trimmed in order to avoid a pollution of the DOM with nasty plain-text elements.

Template Engine Support

The following built-in HTML template languages and corresponding expansion engines are provided out-of-the-box:

  • plain: Plain-Text markup (efficient: pass-through)
    This expands nothing, it just passes through the markup code, without performing any expansions at all. Usually too less useful in practice, but sometimes sufficient enough. It is the default language.

  • simple: Simple markup (efficient: pre-compilation)
    This expands simple variable references like {{foo}} and structured variable references like {{foo.bar[42].quux}}. This often is sufficient in practice.

For more sophisticated features, the following external HTML template languages and corresponding expansion engines are supported out-of-the-box, too:

  • handlebars: Handlebars (efficient: pre-compilation)
  • nunjucks: Nunjucks (efficient: pre-compilation)
  • emblem: Emblem (efficient: pre-compilation)
  • dust: DUST (efficient: pre-compilation)
  • jade: Jade (efficient: pre-compilation)
  • mustache: Mustache (efficient: pre-compilation)
  • walrus: Walrus (efficient: pre-compilation)
  • haml: HAML-JS (efficient: pre-compilation)
  • dot: doT (efficient: pre-compilation)
  • rssi: rssi (efficient: pre-compilation)
  • hogan: Twitter Hogan (efficient: pre-compilation)
  • underscore: Underscore (efficient: pre-compilation)
  • jiko: Jiko (efficient: pre-compilation)
  • qatrix: Qatrix Template (efficient: cached on-the-fly compilation)
  • teddy: Teddy (efficient: pre-compilation)
  • swig: Swig (efficient: pre-compilation)
  • ejs: EJS (efficient: pre-compilation)
  • jst: JST (efficient: pre-compilation)
  • jsrender: JsRender (efficient: pre-compilation)
  • combyne: Combyne (efficient: pre-compilation)
  • kata: Kata (efficient: pre-compilation)
  • markup: Markup (inefficient: on-the-fly compilation)
  • plates: Plates (inefficient: on-the-fly compilation)
  • emmet: Emmet (inefficient: on-the-fly compilation)

For supporting an additional external template engine use a construct like the following:

$.markup.register({
    id:        "handlebars",
    name:      "Handlebars",
    url:       "http://handlebarsjs.com/",
    available: function ()    { return typeof Handlebars !== "undefined" &&
                                       typeof Handlebars.compile === "function"; },
    compile:   function (txt) { return Handlebars.compile(txt); }
});

Check out Template-Engine-Chooser and "What are the best JavaScript templating engines?" to quickly find out which template language fits best for your scenario. My personal preference is to use the built-in Simple language for simple requirements, Handlebars for average requirements and Nunjucks for more complex requirements.

Getting jQuery-Markup

You can conveniently get jQuery-Markup in various ways:

  • Git: directly clone the official jQuery-Markup repository

    $ git clone https://github.com/rse/jquery-markup.git

  • NPM: install as client component via the NPM package manager:

    $ npm install jquery-markup

  • Bower: install as client component via the Bower component manager:

    $ bower install jquery-markup

  • cURL: downloading only the main file from the repository

    $ curl -O https://raw.github.com/rse/jquery-markup/master/jquery.markup.js

Building jQuery-Markup

You can pick the jQuery plugin in file "jquery.markup.js" as is for use, but for linting and minifying it yourself you need Node.js ("node") and its Node.js Package Manager ("npm") globally installed.

# approach 1: use convenient Makefile (author preference)
$ make

# approach 2: use Grunt locally (contributor recommendation)
$ npm install
$ node_modules/grunt-cli/bin/grunt

# approach 3: install and use Grunt globally (contributor alternative)
$ npm install -g grunt-cli
$ npm install
$ grunt

License

Copyright (c) 2013-2015 Ralf S. Engelschall (http://engelschall.com/)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Something went wrong with that request. Please try again.