How to Build a Better Tag Cloud in WordPress

Craig Buckler
Share

Once you’ve defined a great set of tags for your WordPress posts (or pages), you’ll want to display a tag cloud from somewhere in your template. This is normally achieved using the wp_tag_cloud() or wp_generate_tag_cloud() functions which do the hard work for you:

<a href="http://www.mysite.com/tag/word" class="tag-link-7" title="1 topic" style="font-size: 8pt;">word</a>
<a href="http://www.mysite.com/tag/tag" class="tag-link-5" title="2 topics" style="font-size: 14.3pt;">tag</a>
<a href="http://www.mysite.com/tag/phrase" class="tag-link-6" title="4 topics" style="font-size: 22pt;">phrase</a>
<a href="http://www.mysite.com/tag/subject" class="tag-link-4" title="1 topic" style="font-size: 8pt;">subject</a>

Perhaps you’re happy with that. I’m not…

  1. Inline styles? Didn’t we abandon those in 1998?
  2. The classes are pointless. I’ll probably never need to style an individual tag and, even if I do, referencing it by ID is fragile.
  3. I don’t need the fully-qualified URL.

wp_tag_cloud() offers several options but I want more control! As well as addressing the points above, I’d like to assign five or six classes to tags depending on their popularity, e.g. ‘tagged1’ for the least-used tag through to ‘tagged5’ for the most used.

Let’s write a PHP function which returns a customized tag cloud. It can be placed in your theme’s functions.php file (wp-content/themes/<themename>/functions.php) or a plugin.

First, we have our function name which accepts an array of named arguments and sets defaults:

// generate tag cloud
function My_TagCloud($params = array()) {

	extract(shortcode_atts(array(
		'orderby' => 'name',		// sort by name or count
		'order' => 'ASC',		// in ASCending or DESCending order
		'number' => '',			// limit the number of tags
		'wrapper' => '',		// a tag wrapped around tag links, e.g. li
		'sizeclass' => 'tagged',	// the tag class name
		'sizemin' => 1,			// the smallest number applied to the tag class
		'sizemax' => 5			// the largest number applied to the tab class
	), $params));

We now initialize $ret, our returned HTML, and $min and $max — the minimum and maximum number of times a tag is used:

	// initialize
	$ret = '';
	$min = 9999999; $max = 0;

The WordPress get_tags() function is now called. It returns an array of tag objects:

	// fetch all WordPress tags
	$tags = get_tags(array('orderby' => $orderby, 'order' => $order, 'number' => $number));

We now need to determine the the minimum and maximum number of times a tag is used in our site. The following loop sets $min and $max accordingly:

	// get minimum and maximum number tag counts
	foreach ($tags as $tag) {
		$min = min($min, $tag->count);
		$max = max($max, $tag->count);
	}

We can now create our custom tag cloud HTML. We need to loop through all tags a second time and fetch the URL and the link title — a message indicating how many articles use that tag:

	// generate tag list
	foreach ($tags as $tag) {
		$url = get_tag_link($tag->term_id);
		$title = $tag->count . ' article' . ($tag->count == 1 ? '' : 's');

Now for the tricky bit. By default, we want to assign a class ‘tagged1’ for the least-used tag through to ‘tagged5’ for the most used (the class name and numbers can be overridden by setting sizeclass, sizemin and sizemax parameters when calling the function).

We know the minimum and maximum number of times a tag can be used so a little math can determine the class name for us. However, the equation would cause a divide by zero error if each tag was used, say, only once. In that situation, we set the class to just $sizeclass:

		if ($max > $min) {
			$class = $sizeclass . floor((($tag->count - $min) / ($max - $min)) * ($sizemax - $sizemin) + $sizemin);
		}
		else {
			$class = $sizeclass;
		}

We now have enough information to create the HTML for our single tag and end the loop:

		$ret .= 
			($wrapper ? "<$wrapper>" : '') . 
			"<a href="$url" class="$class" title="$title">{$tag->name}</a>" .
			($wrapper ? "</$wrapper>" : '');
	}

Finally, we remove the blog domain URL from $ret, return the value and complete the function block:

	return str_replace(get_bloginfo('url'), '', $ret);
}

The function can be called in any theme file using My_TagCloud();. Arguments can be passed as an associative array, e.g. My_TagCloud(array('orderby'=>'count','order'=>'DESC'));. However, we could also permit content editors to add a tag cloud using a WordPress shortcode, e.g.

// enable [tagcloud] shortcode
add_shortcode('tagcloud', 'My_TagCloud');

In the following example we’ll create a tag cloud within an unordered list:

$tags = OW_Tags(array('wrapper' => 'li'));
if ($tags) {
	echo "<h2>Tags</h2>n<ul class="tagcloud">$tags</ul>n";
}

This produces far tidier HTML code:

<h2>Tags</h2>
<ul class="tagcloud">
<li><a href="/tag/word" class="tagged1" title="1 article">word</a></li>
<li><a href="/tag/tag" class="tagged2" title="2 articles">tag</a></li>
<li><a href="/tag/phrase" class="tagged5" title="4 articles">phrase</a></li>
<li><a href="/tag/subject" class="tagged1" title="1 article">subject</a></li>
</ul>

which is easier to style and maintain CSS:

ul.tagcloud, ul.tagcloud li
{
	font-size: 1em;
	list-style-type: none;
	padding: 0;
	margin: 0;
}

ul.tagcloud li
{
	display: inline;
}

ul.tagcloud a
{
	text-decoration: none;
	padding: 3px 4px;
}

a.tagged1 { font-size: 0.60em; }
a.tagged2 { font-size: 0.80em; }
a.tagged3 { font-size: 1.00em; }
a.tagged4 { font-size: 1.20em; }
a.tagged5 { font-size: 1.40em; }

I hope you find it useful. Please use and adapt the code however you like in your own WordPress projects.