The first jQuery code that you run into at FindNFollow.com is the tag cloud that lets you find Twitter users based on the topics they have tagged themselves with.
The application flow here is pretty simple:
1) User picks an area of interest
2) Clear and reload the tag cloud.
In this post I’m going to dissect how this works from the jQuery perspective.. but first let’s look at the markup since everything starts there.
<h2>1) Select an Area of Interest:
<select id="areaName">
<% foreach (string areaName in ViewData.Model.Areas)
{ %>
<option>
<%=areaName %></option>
<%} %>
</select>
</h2>
</div>
<br />
<h2>2) Click on a tag to find people to follow:</h2>
<div id="tagList">
<ul id="cloud">
</ul>
</div>
This html is a part of as ASP.NET MVC View – similar to a normal ASP.NET aspx page, but we don’t use server controls. For more information on ASP.NET MVC, visit http://asp.net/mvc.
As we can see, the select list drop down box gets populated on the server, but the actual tag cloud does not. All we have is a div (id=tagList) and a unordered list (id=cloud)
As we can see there are no in-line event handlers here – just plain old html (with some in-line code).
We apply the event handlers, the behavior if you will, in the jQuery “document ready” event. A quick note for those who have not used jQuery before – the “$” character is mapped to the jQuery object.
Ok, so here is all the javascript we need to do this.
$(document).ready(function(){
$("select").change(function () {
var str = "";
$("select option:selected").each(function () {
currentArea = $(this).text();
$("#cloud").empty();
//Get the json from the server
$.getJSON(baseurl + "/GetTagsWithTweepsForArea?areaName=" + jQuery.trim(currentArea),
function(data){
$.each(data, function(i,item){
$('<li><a href="' + baseurl + '/item/' + encodeURIComponent(jQuery.trim(item.Name)) + '" class="tag' +
item.Rank + '" >' + item.Name + '</a></li>').appendTo("#cloud");
});
});
});
})
.trigger('change');
});
It looks pretty dense when you are first starting out, but it’s pretty easy really… lets dissect this…
First off, we need to attach this behavior when the document is done loading, so we define a function for jQuery to call when this happens.
$(document).ready(function() {
//stuff to do when the page is loaded
});
This is the basic “do stuff when the page is loaded” construct – basically it’s saying “Hey jQuery, (aka $), when the ready event of the document object fires, do this stuff as well”.
So what do we want it to do? Well, we need to setup an event handler for the select box. Let’s add that in…
$(document).ready(function() {
//stuff to do when the page is loaded
$("select").change(function() {
//Stuff to do when select list changes
});
});
In this case, we only have one select box on the page, so we can attach this event to the select tag. If we had multiple select elements on the page we can target the event more specifically by specifying the id – like so $(”#id-of-the-select-box”).change(function(){ //do stuff});
And what do we want to happen when the select box changes? Well, we need to go and get the tags that match the selected area, and load them into the tag cloud. We’ll take this in steps. Lets get the selected item from the list…
$(document).ready(function() {
//stuff to do when the page is loaded
$("select").change(function() {
//Stuff to do when select list changes
$("select option:selected").each(function() {
var currentArea = $(this).text();
});
});
});
In preparation for reloading the tag cloud, lets clear it out… this is very simple – we just select the tag cloud element, and tell jquery to empty it – with $(”#cloud”).empty
$(document).ready(function() {
//stuff to do when the page is loaded
$("select").change(function() {
//Stuff to do when select list changes
$("select option:selected").each(function() {
var currentArea = $(this).text();
$("#cloud").empty();
});
});
});
Ok, now we need to make an ajax call to a Controller method that will get us the list of tags in this area. For simple “GET” requests, jQuery has a nice simple syntax… ok, it’s not that simple the first time you use it, but it’s still pretty simple…
$(document).ready(function() {
//stuff to do when the page is loaded
$("select").change(function() {
//Stuff to do when select list changes
$("select option:selected").each(function() {
var currentArea = $(this).text();
$("#cloud").empty();
$.getJSON(baseurl + "/GetTagsWithTweepsForArea?areaName=" + jQuery.trim(currentArea),
function(data) {
//stuff to do with the returned data
});
});
});
});
To dissect this a little more, $.getJSON is the jQuery function that will make a XmlHttpRequest (aka XHR) to a specified Url, and then handle the returned data as Json. For this site, we call the GetTagsWithTweepsForArea method on the Tag controller. According to the routing setup in my application, this is on the TagContoller. Instead of hard coding the url, I’m using the baseurl variable to hold the relative part of the url – this allow me to run it on localhost and the live server without changing any code. The actual url is http://www.findnfollow.com/Tag.aspx/GetTagsWithTweepsForArea?areaName=GIS and you can follow that link to see the Json. Here’s a quick capture from FireBug…
We can see that this is an array of “tag” items. Now that we have the data, we need to create the tag elements, and add them into the page.
Conveniently, getJSON does the dirty work of creating objects from the raw json – we just need to iterate over it. And again, there is an easy construct for this.
$(document).ready(function() {
//stuff to do when the page is loaded
$("select").change(function() {
//Stuff to do when select list changes
$("select option:selected").each(function() {
var currentArea = $(this).text();
$("#cloud").empty();
$.getJSON(baseurl + "/GetTagsWithTweepsForArea?areaName=" + jQuery.trim(currentArea),
function(data) {
//stuff to do with the returned data
$.each(data, function(i, item) {
//stuff to do with each item
});
});
});
});
});
So – our tag cloud is housed in an unordered list (<ul></ul>) so we need to create list items (<li></li>) for each tag, and then append them to the un-ordered list. jQuery uses “chaining” to build up complex expressions, and we’ll use that here…
$(document).ready(function() {
//stuff to do when the page is loaded
$("select").change(function() {
//Stuff to do when select list changes
$("select option:selected").each(function() {
var currentArea = $(this).text();
$("#cloud").empty();
$.getJSON(baseurl + "/GetTagsWithTweepsForArea?areaName=" + jQuery.trim(currentArea),
function(data) {
//stuff to do with the returned data
$.each(data, function(i, item) {
//stuff to do with each item
$('<li><a href="' + baseurl + '/item/' + encodeURIComponent(jQuery.trim(item.Name)) + '" class="tag' +
item.Rank + '" >' + item.Name + '</a></li>').appendTo("#cloud");
});
});
});
});
});
So we now have the list items in the list. We can see that we are constructing an anchor (link) inside each tag – these will take the user to the page for that tag. This is made possible because I designed the urls to be meaningful. I use server side code to calculate the relative size of the items by getting the number of users for each tag, then normalizing that, and ranking on a scale of 1 to 10. I then add a CSS class based on the rank.
So that’s about it to handle the change event of the select list. But we want the tag cloud to be loaded up as soon as the user hits the page.
Recall that we are setting all of this up in the $(document).ready event, and that jQuery supports chaining. So, all we need to do is ask jquery to fire the change event…
$(document).ready(function() {
//stuff to do when the page is loaded
$("select").change(function() {
//Stuff to do when select list changes
$("select option:selected").each(function() {
var currentArea = $(this).text();
$("#cloud").empty();
$.getJSON(baseurl + "/GetTagsWithTweepsForArea?areaName=" + jQuery.trim(currentArea),
function(data) {
//stuff to do with the returned data
$.each(data, function(i, item) {
//stuff to do with each item
$('<li><a href="' + baseurl + '/item/' + encodeURIComponent(jQuery.trim(item.Name)) + '" class="tag' +
item.Rank + '" >' + item.Name + '</a></li>').appendTo("#cloud");
});
});
});
}).trigger('change');
});
So – there you have it. The code appears dense, but when you take it in pieces, it’s not too bad.