Advertisement

Building a jQuery-Powered Tag-Cloud

by

A tag-cloud is a great way of showing visitors to your blog the main topics of interest that are available. There is also additional information contained in a tag-cloud. Aside from the actual links themselves, which give people an idea of the subjects that your site covers, they can also show how popular the different subjects are. Another great thing about tag-clouds is that they can be used to describe the frequency of anything; you can link to articles, blog posts, images, video, or anything else that you have in abundance on your site.

tag cloud

Tag-clouds are easy to do badly; whether from a design perspective or from a code perspective. Thanks to jQuery, it’s also easy to do well. We’ll be using the hot new 1.3 version of jQuery for this example and will be working with PHP and MySql to provide a JSON feed of our tags. Getting the tags into a database in the first place is beyond the scope of this tutorial, but it’s a simple enough matter to retrieve and pass them to a waiting page via AJAX.

Getting Started

Let’s make a start on the page that the tag-cloud will be shown on; in a new file in your text editor create the following page:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="tagcloud.css">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>jQuery Tag Cloud</title>
  </head>
  <body>
    <div id="tagCloud">
      <h2>Tag Cloud</h2>
    </div>
    <script type="text/javascript" src="jquery-1.3.min.js"></script>
    <script type="text/javascript">
	$(function() {
	   //get tag feed
	$.getJSON("http://localhost/jquery/tagcloud.php?callback=?", function(data) {
	  //process JSON object			
        });
      });
    </script>
  </body>
</html>

Save this as tagcloud.html. At this stage we have almost nothing on the page, just a simple container for the tag-cloud and a 2nd-level heading within the container. Any other elements we need can be created as and when they’re required. We link to a stylesheet in the head for some styling which we’ll add later on, and at the end of the body we link to jQuery. We make the request for the JSON response in a custom script block after the reference to jQuery.

getJSON

We use the $ alias to call the getJSON jQuery method, which is a higher-level abstraction of the ajax method; normally jQuery methods are called on objects that are references to elements, but because we’re not referencing any elements yet we can use the jQuery alias instead. This also means that the jQuery object will not be returned by the method. Instead the xmlHTTPRequest is passed back.
The getJSON method accepts two arguments in this example (although more can be used if necessary); the first is the URL to which we are making the request. As we’ll be receiving a JSON object, it makes sense to use getJSON. We could use the ajax method, but would then need to configure more properties of the request (such as the dataType), so using this saves us a bit of time and coding. At the end of the URL we specify a JSONP callback - ?callback=? - which will enable the browser to directly manipulate the JSON object, even if it comes from another domain, without any additional server-side processing.

The Callback Function

The second argument is the callback function that we want to execute once the object is returned to the page. We haven’t put any code in this function yet, because we don't have the JSON object to work with. We can come back to this page in a little while once we’ve written the PHP. I said a moment ago that no server-side processing is needed when working with JSONP callbacks, and yet we’re now going to go off and write some PHP. This is only because no one is providing the data we want So we have to create it ourselves. If someone were providing a JSON feed of popular tags, we could still use the same jQuery code to request and process it.

Some PHP

You’ll need to have access to a web server in order to run the file that we’re about to create, but this could be your own local web server that you use for development, or it could be the server your site or blog is hosted on. In a new page in your text editor add the following code:

<?php
    
  //connection information
  $host = "localhost";
  $user = "root";
  $password = "your_password_here";
  $database = "tagcloud";
	
  //make connection
  $server = mysql_connect($host, $user, $password);
  $connection = mysql_select_db($database, $server);
	
  //query the database
  $query = mysql_query("SELECT * FROM tags");
	
  //start json object
  $json = "({ tags:["; 
	
  //loop through and return results
  for ($x = 0; $x < mysql_num_rows($query); $x++) {
    $row = mysql_fetch_assoc($query);
		
    //continue json object
    $json .= "{tag:'" . $row["tag"] . "',freq:'" . $row["frequency"] . "'}";
		
    //add comma if not last row, closing brackets if is
    if ($x < mysql_num_rows($query) -1)
      $json .= ",";
    else
      $json .= "]})";
  }
	
  //return JSON with GET for JSONP callback
  $response = $_GET["callback"] . $json;
  echo $response;

  //close connection
  mysql_close($server);
?>

Save this as tagcloud.php. For this example, I’m assuming you have MySql installed and configured, and have setup a database called tagcloud. Within this database I’m also assuming there is a table called tags. This table will have rows of the tags and the frequency of the occurrences of these tags. I want to stress that this isn’t production-level code because security has not been a factor in its design; we need somewhere to get our AJAX response from in this example and this code will give us that somewhere.

Let’s briefly look at what we've done.

  //connection information
  $host = "localhost";
  $user = "root";
  $password = "your_password_here";
  $database = "tagcloud";

First we setup the connection information that we’ll need in order to connect to the database. Make sure you replace your_password_here with the actual password you set to access MySql. We then connect to the database and set the query that we’ll use to access the data from the tags table.

  //start json object
  $json = "({ tags:["; 
	
  //loop through and return results
  for ($x = 0; $x < mysql_num_rows($query); $x++) {
    $row = mysql_fetch_assoc($query);
		
    //continue json object
    $json .= "{tag:'" . $row["tag"] . "',freq:'" . $row["frequency"] . "'}";

Next we create the string that will start the JSON object, before looping through each row in the table and performing the query. We continue to build the JSON string within the for loop, adding the data from both fields of the current row as properties and values.

    //add comma if not last row, closing brackets if is
    if ($x < mysql_num_rows($query) -1)
      $json .= ",";
    else
      $json .= "]})";
  }

We perform a simple check on each iteration of the loop using the for conditional to see whether we’re reading the last row in the table; if we aren’t we use a comma to separate each object, if we are we close the object. The format of the JSON object will be individual record objects within a single container array, within an outer object.

  //return JSON with GET for JSONP callback
  $response = $_GET["callback"] . $json;
  echo $response;

  //close connection
  mysql_close($server);

We then echo the response back to the client using a GET request; this is needed in order to make use of the jsonp callback in our main page. We need to specify the name of the URL parameter that follows the URL of the in the JavaScript, which in this example is simply callback. We can’t tell it the name of the function that we want to pass it to however, because the function is anonymous. jQuery will handle this for us and ensure the data is passed to the correct function.

Once we’re done, we close the connection. At this stage, we still can’t see anything on the page, but if you run the run from a content-serving directory of your web-server and use the NET tab of Firebug, you can see that data that is being returned to the page:

tag cloud

Processing the json

Now that we have some JSON to work with, let’s go back to the HTML page and do something with it. Our fist task is to process it to extract the data; in tagcloud.html, remove the comment we left within the callback and add the following code:

//create list for tag links
$("<ul>").attr("id", "tagList").appendTo("#tagCloud");
					
//create tags
$.each(data.tags, function(i, val) {
						
  //create item
  var li = $("<li>");
						
  //create link
  $("<a>").text(val.tag).attr({title:"See all pages tagged with " + val.tag, href:"http://localhost/tags/" + val.tag + ".html"}).appendTo(li);
						
  //add to list
  li.appendTo("#tagList");
});

First we create a new list element, set its id attribute, and append it to our container on the page. As the data in the JSON object isn’t in any particular order, an unordered list meets our requirements. Then we use the each() jQuery method to iterate over all of the items in the array nested within our JSON object. For each iteration, we create a new list item and a new link.

We set the text of each link to the value of the tag property of the current object from our JSON object, as well as sett the title and an href. The href used will depend largely on how the pages showing the tags are going to be generated, we could generate a search results style page listing all of the pages that matched whichever tag was clicked using PHP or .NET easily enough (the results page is also beyond the scope of this tutorial). The link is then appended to the list item, and both are appended to the <ul>.

At this stage, our page should appear something like the following:

tag cloud

It’s certainly a list of links, but a tag cloud it isn’t. We can easily fine tune the appearance of the widget with a little CSS. Let’s do this next. In a new file in your text editor, add the following code:

#tagCloud { 
  width:290px; background-color:#575454; text-align:center; padding:5px;
  overflow:auto; font-size:70%; font-family:arial;
}
#tagCloud h2 {
  color:#ffffff; font-size:2.5em; margin:0 0 10px 0;
  background:url(images/cloud.gif) no-repeat 0; padding:15px 0 15px 80px;
}
#tagList { margin:0; padding:0; }
#tagList li {
  list-style-type:none; float:left; margin:0 10px; height:35px;
}
#tagList li a { text-decoration:none; color:#ffffff; }
#tagList li a:hover ( text-decoration:underline; }

Save this as tagcloud.css. The styles used are a mixture of functional and aesthetic rules, such as floating the list items, and setting their dimensions used to control how the widget functions. I’ve kept the styles as minimal as possible, as no doubt you’ll need to change most of the purely visual styles to fit in with the theme of your existing site.

One important point to note is the font-size we’ve used; a font-size of 70% is set on the outer container element; this represents the smallest text that will appear in the tag cloud. We’re going to be adjusting the font size of some tags using em units in the final part of the script. So setting a baseline font-size is important for consistency.

Now when you run the page, it should appear as follows:

tag cloud

Finishing the Script

One of the hallmark attributes of the tags in a tag cloud is that the individual tags are sized according to their frequency of occurrence; the more popular a tag is, the bigger it’s displayed. We can easily make use of the freq property within our JSON object to resize each link according to its popularity. In between creating the new link and appending it to the unordered list in our script, add the following code:

//set tag size
li.children().css("fontSize", (val.freq / 10 < 1) ? val.freq / 10 + 1 + "em": (val.freq / 10 > 2) ? "2em" : val.freq / 10 + "em");

In truth, the css method could easily be chained to the jQuery object directly after we set the link’s title attribute, but they’re separated here for better readability. Within the css method, we specify the fontSize style attribute and use the standard JavaScript ternary conditional to check whether the current value of the freq property divided by 10 is less than 1. If it is, we add 1 to the figure and then concatenate the string em on the end. This will ensure that none of the tags have a font-size of less than 1em, which is equal to our 70% style rule set on the container element.

However if the value of the freq property divided by 10 is not less than 1, we then check (using another ternary, the equivalent of nesting for loops) whether it is greater than 2; if it is, we simply use 2em as the value of the font-size property. Any elements with a font-size of 2em will be twice the size of our original 70% baseline, which is probably as big as any tag in this type of widget should get. Any values greater than 1 but less than 2 are used in their fractional form to set a font-weight of between 1 and 2 ems. The final page should now appear something like the following screenshot when viewed in a browser:

tag cloud

Summary

In this tutorial we’ve seen how "easy" it is to build a basic tag cloud which retrieves the tags to display as part of an AJAX request directly after page load. It is easy to resize each tag depending on its frequency using a sensible range of text sizes. Although the overall appearance of the widget has been left rather minimally styled, it should be easy to build on this foundation to create something that is beautiful as well as functional.

Advertisement