HowTo: Create a simple Tag Cloud for existing HTML content using JS with jQuery

Everybody loves tag clouds. Hopefully, otherwise I spent an hour for nothing creating one for http://www.doerflinger.org.

There are a lot of plugins for jQuery, or external libs etc. for creating tab clouds, most times with fancy AJAX calls, JSON processing and a neat database backend. We only need plain jQuery and for some sexyness the jquery.timer plugin. Please notice that this plugin needs jQuery.plugin to work.

I just wanted to have something which uses my existing HTML list while just extending that with a few tags. My website consists of one single HTML page optically diverted by jQuery tabs and accordions, therefore my main content isn’t visible when the tag cloud is available. So I will just pick some invisible content and display it in an empty space in my page. Another approach could be to just hide everything NOT sporting the chosen tag.

I created a small fiddle at http://jsfiddle.net/zapalotta/bWuFy as an example.

So, this is what I got:

<div id="content">
<h2>Here goes the main content</h2>
<h3>Cats</h3>
<div>
<dt>
<dt>Tiger</dt>
<dd>Big, striped cat</dd>
<dt>Lion</dt>
<dd>Big hairy king of the road</dd>
<dt>Lynx</dt>
<dd>Not just a browser</dd>
</dt>
</div>
<h3>Birds</h3>
<div>
<dt>
<dt>Vulture</dt>
<dd>Quite ugly meateating big bird</dd>
<dt>Dove</dt>
<dd>Sign of love or peace or something</dd>
<dt>Sparrow</dt>
<dd>Rather this than a snail!</dd>
</dt>
</div>
</div>

I would like to display each group of <dt></dt><dd></dd> when their tags are highlighted. Therefore we need some Tags. I decided to use the name attribute, as it is deprecated though still usable in HTML5 without breaking anything. You could of course also use e.g. class or anything self defined.

Tags are separated by blanks as we really only want single worded tags in this case. You can easily change the separator in order to use tags including spaces, though you would need to display the tags with a border and recreate the selector…

<div id="content">
<h2>Here goes the main content</h2>
<h3>Cats</h3>
<div>
<dt>
<dt name="Loud Roaring Carnivore Cat Stripes">Tiger</dt>
<dd>Big, striped cat</dd>
<dt name="Loud Roaring Carnivore Maned Cat">Lion</dt>
<dd>Big hairy king of the road</dd>
<dt name="Cat Brown">Lynx</dt>
<dd>Not just a browser</dd>
</dt>
</div>
<h3>Birds</h3>
<div>
<dt>
<dt name="Carnivore Bird Neck Brown">Vulture</dt>
<dd>Quite ugly meateating big bird</dd>
<dt name="Cute White Bird">Dove</dt>
<dd>Sign of love or peace or something</dd>
<dt name="Bird Small Brown">Sparrow</dt>
<dd>Rather this than a snail!</dd>
</dt>
</div>
</div>

We also need some space for the tags and the highlighted content to be displayed.

<div id="main">
    <div id="cloud"></div>
    <div id="cloudcontent"></div>
</div>

Cloud will contain the tag cloud while cloudcontent will be used to display the chosen content.

So, let’s create some dynamics.

We will create a function consisting of three parts. The variable tags will hold an associative array { “tag” => number_of_occurrences }  with all tags.

function tagcloud() {
   var tags = {};
   ...
}

The first part gathers all tags and fills our associative array with each tag and the number of occurrences.

$( "dt" ).each(function( index ) {
		if ( $( this ).attr ( "name" ) ) {
			$.each( $( this ).attr( "name" ).split (" " ), function( index, value ) {
				if ( !( value in tags) ) {
					tags[ value ] = 1;
				}
				else {
					tags[ value ]++;
					
				}
			});
		}
	});

Here we iterate over all <dt>s, check if they have a name attribute and if so get it’s value and split it into an array.

We now iterate over this array. If the value (i.e. the tag) already exists in our tags list, it increments the number of occurrences of this tag, otherwise it will create a new item with the name of the tag and a number of one.

This is our tags array:

{
  "Loud":2,
  "Roaring":2,
  "Carnivore":3,
  "Cat":3,
  "Stripes":1,
  "Maned":1,
  "Brown":3,
  "Bird":3,
  "Neck":1,
  "Cute":1,
  "White":1,
  "Small":1
}

The next part appends all tags to the cloud div

$.each( tags, function ( name, count ) {
	if ( count > 8 ) count = 8;
	$("#cloud").append("<span class='cloudelement size-" + count + "' id='" + name + "'>" + name + "</span> ");		
});

I created different CSS classes with different shades of grey and font sizes. These are applied to a span surrounding each tag, making the higher number be larger and darker. In order to keep it nice, I limited it to 8 levels.

Now we need to create some dynamics when hovering the tags.

	$('.cloudelement').mouseover( function() {
		$("#cloudcontent").empty();
		$("#cloudcontent").append( "<dl> ");
	    $( "dt[name~='" + $( this ).html() + "']" ).each( function ( index ) {
		   $("#cloudcontent").append( "<dt>" + $( this ).parent().prev().html() + ", " +
		   							$( this ).html() + "</dt>" );
		   $( "#cloudcontent" ).append( "<dd>" + $( this ).next().html() + "</dd>" );
	    });
		$("#cloudcontent").append( "</dl>");
		$('#cloudcontent').timer( 'start' );
	});

As we applied the class cloudelement to all tags, we can simply bind the mouseover() event to them.

We first clear the cloud content div and then append a definition list, then we select all <dt>s where the tag exists in the name attribute and iterate over these. We simply append new <dt></dt><dd></dd>s to our new dl consisting of the <dt>s we have found, the <h3> above it and the following <dd>.

Finally we start a timer which will remove the shown items after five seconds.

The timer will be initialized like this:

	$('#cloudcontent').timer({
		delay: 5000,
		repeat: false,
		callback: function( index ) {
			$( "#cloudcontent" ).fadeOut( "slow", function() {
				$("#cloudcontent").empty();
				$("#cloudcontent").show();
				$('#cloudcontent').timer( 'stop' );	            
			});
        }
    });

So, when time ran out, the callback function will be executed which fades out the complete cloudcontent div, clears it when fading finished and shows it again (empty now) to be prepared to display the next content.

Put the timer initialization together with a call to the tagcloud()  function in the classical $(function() .