How to Build a Better Tag Cloud in WordPress
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…
- Inline styles? Didn’t we abandon those in 1998?
- 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.
- 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.