Dynamically Sizing Text for a Tag Cloud Effect
Tags: javascript, tutorial, jquery
Published: 2018-05-16 06:28:10

---
title: Dynamically Sizing Text for a Tag Cloud Effect
published: true
description: A quick tutorial on how to turn an unordered list into a dynamically-sized tag cloud to display your data!
tags: javascript, tutorial, jquery
---

It's common to see data presented in a tag cloud format—which is to say, a chunk of keywords or terms displayed inline, with each term sized to show its relative importance or frequency out of all the existing terms.

![tag cloud of New York Stock Exchange symbols](https://upload.wikimedia.org/wikipedia/commons/7/79/Top_500_by_volume_on_the_NYSE.png)

A lot of times we see these tag clouds built into content management systems or generated via plugins, but how do we make something like this if we want to do it from scratch? In this tutorial I'll walk through how I built this feature for my own website, to display the relative amounts of time I've worked with different technologies.

## Setting up

I tried to keep the markup pretty simple:

```html
<ul>
  <li data-year="2001">HTML</li>
  <li data-year="2002">CSS</li>
  <li data-year="2003">PHP</li>
  <li data-year="2010">Javascript</li>
  <li data-year="2012">Ruby</li>
  <li data-year="2010">Python</li>
  <li data-year="2017">Node.js</li>
  <li data-year="2010">MySQL</li>
</ul>
```

It's a simple unordered list. The only thing here that's special is that each `<li>` has a data-attribute, `data-year`, which denotes the year I started working with that particular technology. If you were creating a tag cloud for the number of articles with a given tag on your blog, you might instead call the data-attribute `data-count`, and set it to the number of articles with that tag.

Moving on to the styles, again keeping it pretty simple. Mostly I stripped out the default formatting, aligned the content to the center, and set list-items to `display: inline-block` so they coalesce into one block of text.

```css
ul {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  text-align: center;
}

ul li {
  margin: 0 5px;
  display: inline-block;
  vertical-align: middle;
}
```

## The Scripting

First, let's wrap everything in an event handler for the window's `load` event, just to make sure everything's in place before we start applying our font-sizing. I'm using a bit of jQuery here to speed the process along, but there's no reason you couldn't do it in vanilla Javascript.

```javascript
$(window).on('load', function() {
  //...
});
```

If your font sizes change at certain screen size breakpoints, you may also want to re-trigger this on `resize` as well as `load`.

Now that we've got that done, we need to get the range of numbers represented in our tag cloud.

```javascript
  var nums = $('ul li').map(function() {
    return parseInt($(this).data('year'));
  }).get();

  var maxVal = Math.max(...nums);
  var minVal = Math.min(...nums);
```

The `map` function iterates over every list item and returns the value of the `data-year` attribute, parsed as an integer; the `.get()` method formats the output into an array.

We then pass the numbers into Javascript's `Math.max` and `Math.min` functions to get the largest and smallest values, respectively. (`nums` preceded by the `...` to denote that it should be read as an array.)

Now, we'll calculate the sizing for the fonts.

```javascript
  var currentDate = new Date();
  var currentYear = currentDate.getFullYear();
  var baseFont = $('ul li').css("font-size");
  var fontsplit = baseFont.match(/([0-9\.]+)\s?([a-z\%]+)/);
  var minFont = parseInt(fontsplit[1]);
  var maxFont = parseInt(fontsplit[1])*3;
  var fontUnits = fontsplit[2];
```

In this case, we're getting the value of the current year since we want to have the program do the math on how long I've worked with a technology. If you're getting something like a number of blog posts or inventory count instead, you can skip that step!

Then we get the base font size from the CSS. It comes with the units, so I've inserted a regular expression to match a number and a unit abbreviation. The current font size becomes the minimum font size for the tag cloud, assigned to `minFont`; the maximum font size is the minimum times three. You can adjust these to taste—for example, if you want your minimum to be 80% of the base font size, you'd set `minFont = parseInt(fontsplit[1])*.8`.

Now it's time to actually apply the font sizes!

```javascript
  $('ul li').each(function() {
    var itemYear = parseInt($(this).data('year'));
    if (itemYear) {
      var num = currentYear - itemYear;
      var fontSize = (((maxVal - itemYear )/(maxVal - minVal)) * (maxFont - minFont)) + minFont;
      $(this).css("font-size", fontSize+fontUnits);
      $(this).attr("title", (currentYear - itemYear)+" years");
    } else {
      // if we can't determine the year, just set it to 90% size
      $(this).css("font-size", (minFont*.9)+fontUnits);
    }
  });
});
```

First it performs a quick check to make sure that `data-year` is actually set before trying to access it. Then we do a little math, subtracting the value of `data-year` from the current year to determine the years experience (again, this step is skippable if you're not trying to determine years since).

We determine the font size by figuring out what percentage the current number is between the minimum and maximum value, multiplying that by the difference between `maxFont` and `minFont`, and then adding that to `minFont`. We then set the css `font-size` value to that number and tack the units back on at the end.

Also, just to make sure that the information is represented in a way that's accessible to non-visual media, we set the list item's title attribute to "[N] years."

## Final Product

All of this adds up to a result that looks a bit like this!

{% codepen https://codepen.io/cmho/pen/RMMOvo %}

You can also see a live data version on [my personal website](https://carlymho.com/) if you scroll down to the bottom.