Try Tuts+ Premium, Get Cash Back!
Fun with jQuery Templating and AJAX

Fun with jQuery Templating and AJAX

Tutorial Details
  • Topic: JavaScript, jQuery
  • Difficulty: Intermediate
  • Estimated Completion Time: 30 minutes
  • Post Graphic: Available on GraphicRiver

In this tutorial, we’ll take a look at how jQuery’s beta templating system can be put to excellent use in order to completely decouple our HTML from our scripts. We’ll also take a quick look at jQuery 1.5′s completely revamped AJAX module.


What’s Templating?

Templating is a newish (it’s still in beta, so it’s likely to change slightly as it matures and migrates into core, but it’s been around for the best part of a year), hugely powerful jQuery feature that allows us to specify a template to use when building DOM structures via script, something that I’m sure we all do on an almost daily basis.

It’s always been incredibly easy when using jQuery to do something like this:

$("#someElement").children().each(function() {
	$(this).wrap($("<div></div>"));
});

Templating allows us to remove these string-based snippets of HTML from our behaviour.

This will simply wrap each child element of #someElement in a new <div> element. There’s nothing particularly wrong with doing this; it’s perfectly valid and works well in countless situations. But, that’s HTML there in our script — content mixed up with behaviour. In the simple example above it’s not a huge issue, but real-world scripts could contain many more snippets of HTML, especially when building DOM structures with data obtained via an AJAX request. The whole thing can quickly become a mess.

Templating allows us to remove these string-based snippets of HTML from our behaviour layer, and put them back firmly where they belong in the content layer. While we’re doing that, we can also check out one of the brand new, super-cool AJAX features of jQuery 1.5 – deferred objects.


Getting Started

In this example, we’ll build a Twitter widget that will not only load some of our recent tweets, but also list some friends, followers and suggestions. I chose Twitter for this example because it outputs JSON in the format we require; it’s easy and fun.

So let’s get started; the widget itself will be built from the following underlying mark-up:

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="utf-8">
        <title>jQuery, AJAX and Templating</title>
        <link rel="stylesheet" href="tweetbox.css">
        <!--[if lte IE 8]>
			<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
		<![endif]-->
	</head>
		<body>
		<aside id="tweetbox">
			<div id="user">
            	<h2><a href="http://twitter.com/danwellman" title="Visit Dan Wellman on Twitter">Dan Wellman</a></h2>
	            <a href="http://twitter.com/danwellman" title="Dan Wellman"><img src="img/me.png" alt="Dan Wellman"></a>
				<p>Husband, father, front-end developer and author. Writes for Nettuts and Packt Publishing. Works for <a href="http://twitter/.com/designhaus" title="Design Haus">@designhaus</a> | jQuery fanatic :D</p>
			</div>
			<ul id="tools">
				<li><a href="#tweets" title="Recent Tweets" class="on">Tweets</a></li>
				<li><a href="#friends" title="Recent Friends">Friends</a></li>
				<li><a href="#follows" title="Recent Followers">Followers</a></li>
			</ul>
			<div id="feed">
				<div id="tweets">
					<noscript>This widget has super-awesome features which require the use of JavaScript. Please enable it for a better internet experience</noscript>
				</div>
				<div id="friends"></div>
				<div id="follows"></div>
			</div>
		</aside>
		<script src="jquery.js"></script>
		<script src="jquery.tmpl.min.js"></script>
		<script src="tweetbox.js"></script>
	</body>
</html>
Unstyled

We’re using HTML5 and have included the simplified DOCTYPE and meta charset element. We link to a custom style sheet, which we’ll create in a moment, and in order to support current versions of IE8 and lower, we use a conditional comment to link to the Google-hosted html5shiv file.

Using aside

This widget would probably go into a sidebar, and be distinct from the actual content of the page it is featured on, but related to the site as a whole. With that in mind, I feel an <aside> is an appropriate outer container in this case. We give it an id for easy selecting and styling.

Continuing on with our markup, we have some details about the Twitter user whose tweets are listed, including the name in a <h2>, an image and the bio in a standard <p>. Feel free to change these to your own details when reconstructing the example. We could get all of these items from the JSON call that we’ll make when we request the data, however, if there is a slight delay in the request at page load, the visitor could be left staring at a bunch of empty boxes, so hard-coding this info into the widget is again, appropriate. If we were making a plugin for other developers to consume, we certainly couldn’t do this, but when adding this to our own site, or a specific client’s site, this is a feasible approach.

Next, we have the tabs that will be used to switch between the tweets, friends and followers. These are built from a simple collection of <ul>, <li> and <a> elements. The friends tab will be displayed by default, so the link for this tab has the class on attached to it. In a larger project, we could of course use jQuery UI tabs, but I didn’t want the tutorial to lose focus, and it’s no bother to add this functionality ourselves manually.

Notice that we’re also using a plugin – this is the tmpl (templating) plugin, which gives us the ability to use jQuery templates.

Finally, we have the elements that will hold each stream of data; we have an outer container with an id of feed, and three containers for the tweets, friends and followers respectively, which also have id attributes for easy selecting. We also include a <noscript> element for visitors that may have scripting disabled (if any actually exist anymore), which is within the default tab content area. Notice that we’re also using a plugin – this is the tmpl (templating) plugin, which gives us the ability to use jQuery templates. This file can be downloaded from here

Grab a copy now and stick it in the same folder as the web page we just created.

I mentioned a custom style sheet earlier; let’s add that in right now; in a new file in your text editor add the following code:

#tweetbox {
	display:block; width:300px; padding:10px; border:1px solid #aaa; -moz-border-radius:5px;
	border-radius:5px; font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
	background-color:#eee;
}
#tweetbox img { display:block; }
#user { margin-bottom:10px; float:left; }
#user h2 { margin:0 0 10px 0; position:relative; font-size:18px; }
#user img { float:left; }
#user p { width:230px; margin:0; position:relative; float:left; font-size:10px; color:#333; }
#user img { display:block; margin-right:10px; border:3px solid #333; }
#tools { margin:0; *margin-bottom:-10px; padding:0; clear:both; list-style-type:none; }
#tools li {  float:left; }
#tools a {
	display:block; height:20px; padding:3px 24px; border:1px solid #aaa; border-bottom:none;
	-moz-border-radius:5px 5px 0 0; border-radius:5px 5px 0 0; margin-right:-1px;
	position:relative; font-size:14px; outline:none; background-color:#d6d6d6;
	background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0.5, #E8E8E8), color-stop(0, #DBDBDB), color-stop(0.5, #D6D6D6)); 
	background-image: -moz-linear-gradient(center top, #E8E8E8 50%, #DBDBDB 0%, #D6D6D6 50%);
}
a { text-decoration:none; color:#333; }
#tools .on { height:21px; margin-top:-1px; top:1px; }
#feed { width:298px; border:1px solid #aaa; clear:both; background-color:#d6d6d6; }
#feed > div { display:none; }
noscript { display:block; padding:10px; font-size:13px; color:#333; }

Save this file as tweetbox.css in the same directory as the HTML page. This is just a bit of layout styling for our widget. There’s a couple of CSS3 niceties for capable browsers: some rounded-corner action (notice we no longer need the –webkit- vendor prefix for rounded corners in the latest webkit browsers!) and some gradients for the tabs. A point to note is that we hide all of the containers within the feed element, except for the one with the class active. At this point (and with JS disabled) the widget should look like so:


Adding the Script

Let’s put the basic script together and get those tabs working. In another new file in your text editor, add the following code:

(function($) {
	//tabs
	var tweetbox = $("#tweetbox"),
		tweetData = null,
		friendData = null,
		followData = null;

	tweetbox.find("#tools a").click(function(e) {
		e.preventDefault();

		var link = $(this),
			target = link.attr("href").split("#")[1];

		tweetbox.find(".on").removeClass("on");
		link.addClass("on");
		tweetbox.find("#feed > div").hide();
		tweetbox.find("#" + target).show();
	});
})(jQuery);

Save this file as tweetbox.js in the same directory as the HTML page. It’s all pretty straight-forward, and, as it’s not really the main focus of this tutorial, I won’t go into it too much. All we do is alias the string character within an anonymous function, which we execute straight away – more for good-practice than sheer necessity in this example – and then cache a selector for the main outer container for the widget. We also initialize three variables for use later on, and set their values to null.

We’ll need to select elements repeatedly throughout the code; so caching a reference to the outer container helps minimise the number of jQuery objects we need to create. We then set a click handler for the tab links which gets the id of the tab to show from the href of the link that was clicked, removes the class name on from the tab links and then adds it back to the link that was clicked. We then hide all of the tab panels, before showing the selected tab panel.


Getting the Data

Now the real fun begins; we can make the requests to Twitter to get our three data sets and make use of jQuery’s templating plugin to create the required DOM elements using the data we obtain from the requests. We’ll get the data first and then add the templates. After the click-handler for the tool links, add the following code:

$.ajaxSetup({
	dataType: "jsonp"
});

function getTweets() {
	$.ajax("http://api.twitter.com/statuses/user_timeline/danwellman.json", {
		success: function(data) {
			var arr = [];
				
			for (var x = 0; x < 5; x++) {
				var dataItem = {};
				dataItem["tweetlink"] = data[x].id_str;
				dataItem["timestamp"] = convertDate(data, x);
				dataItem["text"] = breakTweet(data, x);
				arr.push(dataItem);
			}
				
			tweetData = arr;
		}
	});
}
function getFriends() {
	return $.ajax("http://api.twitter.com/1/statuses/friends/danwellman.json", {
		dataType: "jsonp",
		success: function(data) {
			var arr = [];
				
			for (var x = 0; x < 5; x++) {
				var dataItem = {};
				dataItem["screenname"] = data[x].screen_name;
				dataItem["img"] = data[x].profile_image_url;
				dataItem["name"] = data[x].name;
				dataItem["desc"] = data[x].description;
				arr.push(dataItem);
			}
			
			friendData = arr;	
		}
	});
}
function getFollows() {
	return $.ajax("http://api.twitter.com/1/statuses/followers/danwellman.json", {
		dataType: "jsonp",
		success: function(data) {
			var arr = [];
				
			for (var x = 0; x < 5; x++) {
				var dataItem = {};
				dataItem["screenname"] = data[x].screen_name;
				dataItem["img"] = data[x].profile_image_url;
				dataItem["name"] = data[x].name;
				dataItem["desc"] = data[x].description;
				arr.push(dataItem);
			}
				
			followData = arr;
		}
	});
}
	
//execute once all requests complete
$.when(getTweets(), getFriends(), getFollows()).then(function(){
		
	//apply templates	
});

First, we use jQuery’s ajaxSetup() method to set the dataType option to jsonp for all subsequent requests. As this will be the dataType used by each of our requests, it makes sense to just set the option once.

We then define three standard functions; within each function we use jQuery’s ajax() method to make a request to the web service that returns each set of data we’ll be working with, the user_timeline, friends and followers respectively. In the settings object for each request, we define a success handler which will be executed once each individual request returns successfully. Each of these requests will return a JSON object that potentially contains up to 100 objects packed full of Twitter data.

To ensure that the data is stored in the correct format for JSON we use square-bracket notation.

We don’t need that much data, so in each success handler we create a new array, which in turn will contain a series of objects that hold just the data we are actually going to use. To ensure that the data is stored in the correct format for JSON, where every property name must be a string, we use square-bracket notation to set the property names in string format.

The user_timeline request stores the id string of the tweet which can be used as part of a URL that points to the tweet, as well as storing the result of two utility functions. The first of these functions creates a formatted data string which converts the date returned by Twitter into something that is a little prettier, and localised to the viewer’s machine. We also format the tweet text so that we can atify any @usernames found in the text. We’ll look at both the date and tweet formatter functions shortly.

The functions to retrieve the friends and followers lists are pretty much identical. Again, we rename the properties that we’ll be working with when we build our objects and store them in each array. All three of our success handlers store the resulting 5-item arrays in the null variables we set at the top of the script.

Notice that we don’t invoke each of our getTweets(), getFriends() and getFollowers() functions manually. Instead, we use jQuery’s new when() method to invoke all of them at the same time. This method will completely handle running these functions and will keep track of when each one has returned. We chain the then() method after the when() method. Once all of the specified functions have returned successfully, the callback function we pass to the then() method will be executed.

The when() method creates a deferred object that manages the functions we specify as arguments.

Once the functions have all returned, the deferred object is resolved and any functions registered with then() are called. Other handlers may also be chained to the when() deferred object, such as fail(), which would be executed if one or more of the functions passed to the deferred object failed.

This is incredible; we want to make all three requests, but we have no way of knowing beforehand which of these will be returned last, so it is impossible to use any one request’s callback function if we wish to process the data returned from all of the functions at the same time. In the past, we would probably have had to setup an interval which repeatedly polled each function to check whether it had returned, and wait until all of them had before proceeding. Now we can delegate all of this manual tracking to jQuery to handle for us automatically.


Utility Functions

We use two utility functions in this example: convertDate() and breakTweet(). The code for these functions is as follows:

//format date
convertDate = function(obj, i) {
		
	//remove time zone offset in IE
	if (window.ActiveXObject) {
		obj[i].created_at = obj[i].created_at.replace(/[+]\d{4}/, "");
	}
		
	//pretty date in system locale
	var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
		date = new Date(obj[i].created_at),
		formattedTimeStampArray = [days[obj[i].created_at], date.toLocaleDateString(), date.toLocaleTimeString()];
			
	return formattedTimeStampArray.join(" ");
}
	
//format text
breakTweet = function(obj, i) {

	//atify
	var text = obj[i].text,
		brokenTweet = [],
		atExpr = /(@[\w]+)/;

	if (text.match(atExpr)) {
		var splitTweet = text.split(atExpr);
		
		for (var x = 0, y = splitTweet.length; x < y; x++) {

			var tmpObj = {};
				
			if (splitTweet[x].indexOf("@") != -1) {
				tmpObj["Name"] = splitTweet[x];
			} else {
				tmpObj["Text"] = splitTweet[x];
			}
			
			brokenTweet.push(tmpObj);
		}
	} else {
		var tmpObj = {};
			tmpObj["Text"] = text;
		brokenTweet.push(tmpObj);	
	}
		
	return brokenTweet;
}

The convertDate() function is relatively straight-forward: we first check whether the browser in use is a variant of IE by looking for window.ActiveXObject. If this is found, we use the JavaScript replace() method to remove the Timezone Offset supplied as part of the string contained within the created_at property of the JSON object returned by Twitter. This method takes the regular expression pattern to look for, and an empty string to replace it with. This stops IE choking on the + character when the string is passed to the new Date constructor.

Next we create some variables; we set an array containing shortened day names, with Sunday (or Sun) as the first item in the array. Days in JavaScript dates are zero-based, with Sunday always appearing as day 0. We then create a Date object using the new Date() constructor, and pass in the date string stored in the created_at property of the object we passed in to the function.

We then create another array containing three items: the first item gets the correct day of the week from the first array we created within this function, the next item gets the localised date, and the last item gets the localised time. Finally, we return the contents of the array after we have joined it. We could simply use string concatenation to build this date string, but joining array items is much faster than building strings manually.

The breakTweet() function is slightly more complex. What we need to do is convert the plain text into a JSON array where each array item is an object containing either a Name or Text property so that we can use the data with a template (more on the templates next). First we store the text from the object returned by Twitter (which we pass into the function). We also create an empty array to store the objects in and define the regular expression that will match @usernames.

We then test the text to see whether it contains any usernames; if it does we then split the text string on each occurrence of a username. This will give us an array which contains items that are either plain text, or a username. We then cycle through each item in this array and check whether each item contains the @ symbol; if it does, we know it’s a username and so store it in an object with the key Name. If it doesn’t contain the @ symbol we save it with the key Text. The object is then pushed into the array. Also, if the whole text doesn’t contain an @ character we store it with the key Text.

That’s it; once we have stored our objects the function returns the new brokenTweet array to the user_timeline success function and is stored in the main JSON object for use with our templates. As well as atifying the text, we could also linkify and hashify if we wanted. I’ll leave that up to you to implement.


Templating

Now that we have our processed JSON data, we can move on to the final part of the example: templating. In the anonymous function passed to the then() method in the last code section, there was a comment and nothing else. Directly after this comment, append the following code:

//apply templates
tweetbox.find("#tweetTemplate").tmpl(tweetData).appendTo("#tweetList");
tweetbox.find("#ffTemplate").tmpl(friendData).appendTo("#friendList");
tweetbox.find("#ffTemplate").tmpl(followData).appendTo("#followList");
		
//show tweets
tweetbox.find("#tweets").show();

This code simply applies the templates using the jQuery templating plugin method tmpl(). The method accepts the JSON containing the data that the template will use. We then specify where in the document to insert the template elements. Each set of templates appends the elements to the respective empty container within the widget. The tmpl() method is called on three elements, but these elements don’t yet exist. We’ll add these next.


Adding the jQuery Templates

Switch back to the HTML and first add the following <script> element directly after the empty <ul> with the id tweetList:

<script id="tweetTemplate" type="text/x-jquery-tmpl">
	<li>
		<p>
			{{each text}}
				{{if Name}}
					{{tmpl(Name) "#atTemplate"}}
				{{else}}
					${Text}
				{{/if}}
			{{/each}}
			<a class="tweet-link" href="http://twitter.com/danwellman/status/${tweetlink}">${timestamp}</a>
		</p>
	</li>
</script>
<script id="atTemplate" type="text/x-jquery-tmpl">
	<a href="http://twitter.com/${$item.data}">${$item.data}</a>
</script>

jQuery templates are added to the HTML page using <script> elements. These elements should have id attributes set on them so that they can be selected and have the tmpl() method called on them. They should also have the custom type x-jquery-tmpl set on them.

In the first template, we add the mark-up that we want to new DOM structure to be built from, which in this case is an <li>, a <p> and an <a> to create each tweet. To insert the data from the JSON object passed into the tmpl() method we use a series of templating tags. First we use the {{each}} tag to go through each item in the text array.

This is the array containing the broken up tweet, so for each object we check whether it has a Name key; if it does we use the {{tmpl}} tag, which allows us to use a nested template. We specify the data to pass the nested function within parentheses after the tmpl tag and also specify the id of the template to select (this is the second template that we just added, which we’ll look at in more detail in a moment). If the object does not contain a Name property, we know that we are dealing with a plain text portion of the tweet and just insert the data using ${Text}. This conditional is achieved using the {{else}} template tag. We should also close the conditional using {{/if}, and close the iteration using {{/each}}.

Finally, we create a new anchor element that links directly to the tweet on the Twitter website using ${tweetlink} as part of the href, and ${timestamp} properties. These properties are the ones we created in the success handler for the user_timeline request.

In the atTemplate we also create a link; this time it links to the user that was mentioned. As this is a nested template, we need to access the actual data slightly differently; the data passed into the nested template by the {{tmpl} tag will be stored in a property of the $item object called data.

We still need to add the template for our friends and followers tabs. Both of these will be built from the same template, which should be as follows:

<script id="ffTemplate" type="text/x-jquery-tmpl">
	<li>
		<p>
			<a class="img-link" href="http://twitter.com/${screenname}"><img src="${img}" alt="${name}"></a>
			<span class="username"><a href="http://twitter.com/${screenname}">${screenname}</a></span>
			<span class="bio">${desc}</span>
		</p>
	</li>
</script>

This template is much simpler as we aren’t using nested templates or doing any iteration, we’re simply inserting the data from each JSON array using the ${data} template item format.


Finishing Up

Now that we applied the templates and populated our widget, we can add a few more CSS styles to tidy up the new elements that have been added; in tweetbox.css add the following code to the end of the file:

#feed ul { padding:0; margin:0; }
#feed li { padding:0; border-bottom:1px solid #aaa; list-style-type:none; font-size:11px; }
#feed li:last-child, #feed li:last-child p { border-bottom:none; }
#feed p { padding:10px; margin:0; border-bottom:1px solid #eee; background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0.48, #c2c2c2), color-stop(0.71, #d6d6d6)); background-image:-moz-linear-gradient(center bottom, #c2c2c2 48%, #d6d6d6 71%); }
#feed p:after { content:""; display:block; width:100%; height:0; clear:both; }
.tweet-link { display:block; margin-top:5px; color:#777; }
.img-link { display:block; margin:4px 10px 0 0; float:left; }
#feed .username a { font-size:14px; font-weight:bold; }
#feed .bio { display:block; margin-top:10px; }

Save the file, our page should now appear as follows:

There’s still one more thing we should probably do: at the moment, our tweet formatting function doesn’t work in IE because of how IE treats the split() regular expression method. To fix this issue, we can use an excellent JavaScript patch created by Steven Levithan. It can be downloaded from: http://blog.stevenlevithan.com/archives/cross-browser-split and can be included in the page using a conditional comment in the same way that we added the html5shiv file:

<!--[if IE]>
	<script src="fixSplit.js"></script>
<![endif]-->

This should be added directly before the script reference to our tweetbox.js file.


Summary

In this tutorial we looked at some of the more advanced features of jQuery templating, such as item iteration with {{each}} and conditionals with {{if}} and {{else}}.

A key point is that the new templating system from jQuery allows us to better separate our behaviour and presentation layers, moving all HTML snippets out of the .js file and back into the .html file.

Some might argue that we now just have the opposite situation and simply have more <script> in our page.

However, I think this is infinitely preferable to the alternative. Elements in the format $("<div>") are simply strings with no implicit meaning (until jQuery is run by the browser and they are created and inserted into the DOM).

We also looked at the excellent new when() method provided in jQuery 1.5, which allows us to manage a set of asynchronous requests and execute code once they have all completed. This involves using the then() method to specify a function to execute once the asynchronous functions have all returned.

Thank you so much for reading and let me know if you have any questions!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://ran.yefet.net Ran

    Great Tutorial!

    How is it affecting HTML Validation & SEO?

    • http://www.danwellman.co.uk Dan Wellman

      It shouldn’t affect validation because the templating tags are in script blocks. In a widget such as this it should have a negative impact on SEO. You probably wouldn’t want to build the primary page content using this technique though

      • http://www.danwellman.co.uk Dan Wellman

        so that’s *shouldn’t* have a negative impact on SEO…

  • http://blog.brianseitel.com Brian

    tl;dr

    It’s not that this wasn’t an informative article, it’s that it wasn’t until 2/3rds of the way through the (really, really, really long) article that you actually started talking about templating via jQuery. Maybe a simpler example would have been more useful, instead of starting with what essentially amounted to a jQuery Twitter app tutorial with templating thrown in as an afterthought.

    On the upside, I did discover some neat functions I didn’t know about earlier, so thanks for that.

    • http://scott-christopherson.com Scott

      These were my same thoughts as well. It’s very informative but a much simpler example would have been more effective for me.

  • http://www.martaspendowska.com Marta Spendowska

    Great tutorial.
    Tuts always provides such a value.
    Anxious to try it out. Thanks!

  • http://matthewfowles.com Matthew Fowles

    Great article nice technique! definitely gonna use this thanks

  • http://noxxten.com Dustin

    I have to agree with Brian above. The article seems really drawn out and long for what it’s teaching. But a very nice job explaining it all regardless. Kudos for producing an actual useful example too.

    • http://www.danwellman.co.uk Dan Wellman

      @Brian and @Dustin: apologies the article was so long. It wasn’t just about the templating though (and that was far from being thown in as an afterthought) – it also covered the when() and then() brand new jQuery 1.5 AJAX functionality, as well as some CSS3 and HTML5. It needed to be as long as it ws to get all that in, but 2 or three separate tutorials to cover each aspect individually would have been too short…

  • Bohdan Ganicky

    “Some might argue that we now just have the opposite situation and simply have more in our page.”

    Actually, you don’t have to add templates to the document with script tags at all. You can define templates in your .js file or even load it from an external template file. For example:

    var data = [
    { name: "someName", age: 16 },
    { name: "anotherName", age: 32 }
    ];
    var template = “${name}, ${age}”;

    $.template( “person”, template );

    $.tmpl( “person”, data ).appendTo( “#people” );

    or:

    $.get( “./tmpl/person.tmpl”, function( tmpl ) {
    // create a compiled named template
    $.template( “person”, tmpl );
    // get actual data to render
    $.getJSON(“path/to/data”, function( json ) {
    // render the data with person template
    $.tmpl( “person”, json ).appendTo( “#people” );
    });
    }, “text”);

    • http://www.danwellman.co.uk Dan Wellman

      Great tip, thanks for sharing :)

    • http://efekat.com Ivan

      +1

      • http://erichynds.com Eric Hynds

        Deferreds FTW:

        $.when( $.get(‘./tmpl/person.tmpl’), $.getJSON(‘path/to/data’) )
        .then(function( template, data ){
        $.tmpl( template, data ).appendTo( “#people” );
        });

      • http://lloydsheng.com lloydsheng

        +1

  • http://www.joshuakelly.com.au Josh

    I have to agree with Brain also, Great article and it is quite useful but does feel bloated in it’s delivery.

    Thanks for taking the time to write. I enjoyed the read.

  • http://www.londonswebdesign.com Michael Clark

    Really nice article. It may be, as Brian points out above, a long article, but it does the job and there is quite a lot of ground to cover in the scripts and the CSS. Seriously nice work on the code, as well. I used to teach JavaScript and I would definitely give you a high grade for clean, easy-to-read code.

    Thanks for taking the time to share.

    -Michael

  • http://mimrankhan.com Imran Khan

    Nice Article, i will use this to my website.

  • Sandy

    Excellent!

  • http://beben-koben.blogspot.com/ Beben Koben

    wow…its a good

  • http://www.indian-seo-company.com/ Andrew

    Good coding..

  • http://www.simianenterprises.co.uk Gary Stanton

    Some really impressive techniques in here – excellent tutorial, thanks!

  • http://www.csskarma.com Tim Wright

    My problem with jQuery templating has always been the implementation of it. As to my understanding (and mentioned in the article), a large purpose behind using it is to get HTML out of your javascript. I’m all for that, but not when you’re just trading it off to embed javascript in your HTML… it’s basically the same problem except that now anyone without JS enabled has to download all the markup they can’t use.

    Don’t get me wrong, I actually think it’s very cool. But I think it really belongs on the server rather than the client.
    I’d be 100% onboard using jQuery templating as server-side javascript.

    Generally, embedding your behavior in your structure isn’t a good idea, I mean all learned that from the progressive enhancement model.

    Overall, I do really like the concept behind jQuery’s templating though, let’s use it server-side!

    • http://www.csskarma.com Tim Wright

      “implementation” may not be the right word…. more-so “how the use cases have been presented”

    • http://www.danwellman.co.uk Dan Wellman

      I can see your point, although I don’t agree entirely. jQuery templating as server-side javascript certainly would be awesome though :)

      The script element is a valid HTML element, and the template itself isn’t behaviour, it’s structure. The template is still applied by the behaviour.

      Also, there doesn’t have to be much extra markup to download. Sure, in this example, there are a bunch of containers, an image and some text, the tabs, etc, but that was all for the purpose of the demonstration. The tutorial was long enough as it was without covering building the entire widget from script as well. You could just use a single container on the page, with the template script inside it, and build any other required elements progressively with JS, or use another template. The templates themselves are tiny, so there isn’t a huge amount to download (the total size of the templates used in this example is 771 bytes) for the tiny fraction of internet users that have JS disabled.

  • http://twitter.com/ljharb Jordan

    “To ensure that the data is stored in the correct format for JSON, where every property name must be a string, we use square-bracket notation to set the property names in string format.”

    I’m not sure where you got this idea – Javascript object property names are always strings. Dot notation is perfectly acceptable here, and is generally the preferred method. Can you provide an example, barring using a reserved word as a property name, where dot notation doesn’t give you the desired result?

    • http://www.danwellman.co.uk Dan Wellman

      I can’t think of a specific example off the top my head, but I’m sure I’ve had a situation before where using square bracket notation has been the only way…

      But you’re right about this example, dot notation is equally valid here.

      • http://twitter.com/ljharb Jordan

        Since square bracket notation reduces the effectiveness of JS minifiers and packers, I suggest removing the part of the article that advocates its use. The only time square bracket notation should be used, as far as I am aware, is when the property name is a reserved word, like “class”, “if”, et cetera.

      • http://www.danwellman.co.uk Dan Wellman

        There are definitely occasions when square bracket notation is necessary. As I said, I can’t think of any specific examples right now, but I’ve definitely come accross it before. A quick Google shows some information about when to use square bracket notation, for example: http://www.javascripttoolbox.com/bestpractices/#squarebracket

        Square bracket notation is an entirely valid form of accessing properties on an object and many consider it preferable to using dot notation. Dot notation is valid in *most* situations, but there are times when it fails, whereas square bracket notation is *always* valid. Square bracket notation can be used wherever dot notation can, but dot notation cannot be used wherever square bracket notation can.

        Minifying the tweetbox.js file used in this example makes absolutely no difference to the example’s functionality in the scripts existing form. Packing the script breaks it whether square or dot notation is used. I’m not sure why this is, it could be the code, it could be something to do with templating..?

        Personally I prefer minifying scripts than packing them anyway, and to be honest, it’s not something I do very often these days – most of the sites I build have their scripts minified at runtime by the server (using something like .Net’s Client Dependency Module) when served to the client…

        You’ve raised an interesting point for sure, but as square bracket notation seems to make absolutely no difference whatsoever in this example, I don’t feel it necessary to change anything.

      • http://twitter.com/senthilnathansk sk

        square bracket notation is needed when you property name contains space..
        var obj = { “Valid Prop” : true };
        obj["Valid Prop"] works. but not obj.Valid Prop

      • Kris

        Square bracket notation is incredibly useful in a variety of situations; as well as property names that include spaces, properties that have any kind of special character in it (such as cssClasses[".box-wrapper"]) and dynamically referenced properties (function(prop){ return object["prefix_"+prop] }) are common circumstances

    • shamsi

      that’s cool :)

  • http://aggregatestudios.com Jeremy Bayone

    Maybe a little wordy, but otherwise a good tutorial. Interesting to see where jquery templating goes since it’s still in the beta phase.

  • http://kathleenbjoran.com/ Kathleen Bjoran

    I am not getting the desired outcome here. I tried using my screen name (as shown in the walk-through) but my info won’t populate the box so I used my numerical ID but that won’t work either. I’ve checked my settings at Twitter and my little Tweet Privacy is not ticked. I can enter other folk’s user names and/or numerical IDs into the scripts and their info shows up. Any suggestions?

    http://kathleenbjoran.com/mine/tweetbox.html

    • http://www.kathleenbjoran.com Kathleen Bjoran

      Okay…I figured it out. I don’t yet have 5 friends so I just had to change some of the variables. :-)

  • http://www.wordimpressed.com Devin Walker

    Awesome tut. Showed me a bit about jQuery’s templating

  • http://zagalski.pl Zagal

    Great and useful article!

  • http://ecustom.ca/ Damon Bridges

    Just a note, in the getFriends function AJAX call, you are redefining the dataType as jsonp, when you have already declared that in $.ajaxSetup.

    Great tutorial Dan!

  • http://www.mmog.at/ Gam.

    I always avoided using jQuery because it increased the load-speed of all of my pages by more than 50 percent. I am the type of webdesigner who wants to make the viewing of a website as smooth as possible, so I try to avoid complex scripts, images and other files.

  • http://souravchakraborty.co.nr Sourav Chakraborty

    Excellent tutorial. Very well explained. Thank you Dan.

  • http://tonkalabs.com/en Ricardo

    Hi! Nice tut!
    I also use the jquey detach() method, so the template cannot been seen in the source code…
    it would be somthing like:


    tweetbox.find(“#tweetTemplate”).detach().tmpl(tweetData).appendTo(“#tweetList”);…

    • http://www.danwellman.co.uk Dan Wellman

      Brilliant tip Ricardo +10 :D

      Thanks for reading

    • Kris

      While using detach will certainly remove the template from the DOM, it won’t stop it from being viewable in the source (as in the static ‘view source’ not the live DOM representation in dev tools like firebug) so there’s no great point in doing so. You’d be better either loading the templates from an external file or defining them programmatically if that’s what you’re trying to achieve.

  • http://www.yusufakyol.com Yusuf Akyol

    Hi,

    Thanks for sharing all your works and this tutorial also.

    But at IE8 and below there are some problems, I think “html5shiv” causes this.

    Best regards.

    • http://www.danwellman.co.uk Dan Wellman

      Yusuf, thanks for reading :)

      What problems are you seeing in IE? I can view the demo attached to this tutorial in IE8 perfectly, with just some minor degredation in the overall presentation (no rounded corners, no gradients)

  • http://andymatthews.net andy matthews

    You’ve got a misspelling. In the paragraph directly above the “Using aside” header, you call it the “Google-hosted html5shiv file” instead of “Google-hosted html5shim file”.

    I don’t think that Google will be hosting a shiv anytime soon.

    :)

    • http://www.danwellman.co.uk Dan Wellman

      Andy, a shim is something entirely different (a transparent iframe used to cover select boxes in older versions of IE when using things like modal popups)

      The file on Google code is called html5shim, but the author of the script, Remy Sharp, calls it the html shiv file in his blog, as does John Resig on his blog

      http://remysharp.com/2009/01/07/html5-enabling-script/
      http://ejohn.org/blog/html5-shiv/

      thanks for reading :)

  • http://andymatthews.net andy matthews

    @Dan…

    The problem is that the title of the article is “Fun with jQuery Templating”. Not “Using .when(), .then(), and .tmpl()”.

    The article was well written, but you should always go for the simplest example that communicates your intended point.

  • Pablo Z

    for (var x = 0; x < 5; x++) {
    var dataItem = {};
    dataItem["screenname"] = data[x].screen_name;
    //…

    “”"
    To ensure that the data is stored in the correct format for JSON, where every property name must be a string, we use square-bracket notation to set the property names in string format.

    “”"

    Wrong (and misleading). You do so just because x is a variable.

    • http://www.danwellman.co.uk Dan Wellman

      Pablo, it is not because x is a variable. dataitem.screenname = data[x].screen_name works just as well, as mentioned in the comments nearer the top. Although square bracket notation is required sometimes when working with variables used for object keys in dynamically created objects, which is where the confusion seems to stem from.

      But yes, you are correct that the wording of that section is misleading and for that I apologise

      Thanks for reading :)

  • http://mo.notono.us Oskar Austegard

    Nice, practical example of when() and then(), if not necessarily of templating itself…

    There’s a typo in the code for the Getting the Data section – getTweets() should return the $.ajax() function, not just invoke it.

    • http://www.danwellman.co.uk Dan

      well spotted, thanks for reading :)

  • http://www.bibledove.com/ bible study

    Hi

    Many thanks for taking the time to discuss this, I really feel strongly about it and love learning much more on this topic. If possible, as you gain expertise, would you mind updating your website with a lot more facts? It’s extremely helpful for me.

    Thanks :)

  • http://www.livepage.info Livepage

    Very useful tutorial, thanks!

  • Kostya

    I don’t understand the reason of using nested templates here.
    Name property is not array so replacing ” {{tmpl(Name) “#atTemplate”}} ”
    for: “${Name} ” will give same result.

  • Kostya

    I don’t understand the reason of using nested templates here.
    Name property is not array so replacing {{tmpl(Name) “#atTemplate”}}
    for: ${Name} will give same result.

  • Kostya

    I don’t understand the reason of using nested templates here.
    Name property is not array so replacing {{tmpl(Name) “#atTemplate”}}
    for: &lta href=”http://twitter.com/${Name}”&gt${Name}&lt/a&gt will give same result.

  • Kostya

    I don’t understand the reason of using nested templates here.
    Name property is not array so replacing {{tmpl(Name) “#atTemplate”}}
    for: <a href=”http://twitter.com/${Name}”>${Name}</a&gt ; will give same result.

  • Thomas

    Hey Dan,

    How do you go about addressing this issue?

    {“request”:”\/statuses\/user_timeline\/tgbeazy.json”,”error”:”Rate limit exceeded. Clients may not make more than 150 requests per hour.”}

    I’m not sure how Twitter would be getting hit that often in my case… But I get the cursed blank screen after twiddling for 5 minutes.

    Thoughts?

    Thanks
    Thomas

  • http://anjumrizwi.blogspot.com/ Anjum Rizwi

    Excellent Article!!!

    I agree it is long article but it covers all area of jQuery template. After reading this article, I am comfortable in using template. No need to read other article.

    Well covered when, then and nested template with example step by step.

    Specially thanks to all those who has commented and well replied answer.

    If anybody is beginner in jQuery they can also use jQuery template after reading this.

    Finally I would say about long article To get more we have to pay more.

    I would name this article jQuery Template eBook

  • http://pushinpixels.co.uk Paul

    Is there a bug with this demo in IE ?

    • http://pushinpixels.co.uk Paul

      Windows 7 update resolved the problem.

  • Mike

    There’s also a plugin that helps handle template caching for external files. It does the ajax call, automatically determines template names, can do multiple templates at once, and can use selectors for multi-templated files. It returns a Deferred, so it fits in with this tutorial. Samples:

    $.fetcher(“twitter.html”).done(callback); // makes template called “twitter”
    $.fetcher(“twitter.html”, “social”, callback); // custom name
    $.fetcher(“template.html #twitter”, callback); // makes template called “twitter”

    https://github.com/codeinfused/Fetcher

  • rithi

    Anyone send me the integrated code for chat box(like login with nickname) with all utilities. I’m having the code.But i don’t how to link them all.

  • http://richard-dickinson.com Richard

    Hi
    I really like this-thanks.
    Now I want to add some extras (a tweetbox / different styling etc) and make it really personal for me.
    I’m still not sure about jQuery Templating & Ajax but I do like the Twitter client output!
    I will look into templating after I get my webpage Twitter interface sorted…
    Thanks again & best wishes

  • http://www.browsergames-4u.de Markus

    Thanks for the nice article and for sharing the whole source! I’ll use it on my site with some modifications i’ll make on my own.

  • Dave

    Hi.
    Have you test this library on FF 10? I was checking it and it is not working – I think that the risen is using which is not recognized by FF.
    Best regards

  • Michael Kintu

    Very clear and interesting to read. Thanks Dan. Ill follow up all your articles. Michael (Kampala, Uganda).

  • Tuhin

    Doesn’t work at all…no data is received on clicking any of the tabs…on firebug console it gives 404 error…on call back function.

  • http://www.facebook.com/profile.php?id=100004569301828 Carlos Montoya

    sdd

  • http://twitter.com/bassemelkahki Bassem El Kahki

    This will no longer work as Twitter’s API v1.0 has been officially removed – now updating to 1.1. The new API requires sort of an Ouath system for access. So if anyone is getting errors, that’s why!