Dan Schlosser / writing /

Who Likes Integers, Anyway?

Who Likes Integers, Anyway?
Published on November 22, 2013.

It’s a well documented phenomenon: I spend too much time tweaking my personal site. I’ve spent enough time working on the current iteration that even the smallest quirks or bugs get on my nerves.

At the top of that list was a problem with the rotating tagline sentences. It’s a cool effect, but a pain to execute. Here’s a quick summary of how the animation works:

  1. Fade out all the words that are different between each set of subsequent sentences.
  2. For each word that is getting replaced, expand or contract the space where it just was to be the size of the new word.
  3. Fade in the new word, which now has a container of perfect size.
Who likes integers anyway

The tricky part of this effect ends up being resizing the container to be the exact size of the upcoming word. Setting the container to have width: auto in CSS will allow it to fit the size of it’s contents, but there is no way to animate between two different sizes that result from using width: auto on different words. To perform the animation appropriately, I perform a more complicated set of operations:

  1. Calculate the width of the old word, call that oldWidth.
  2. Set the width of the container to be exactly oldWidth. (This results in no visual change, because it’s the same width as auto, but it changes how the width is calculated.)
  3. Fade out the old word.
  4. Calculate the width of the new word, call that newWidth.
  5. Animate the container from oldWith to newWidth.
  6. Fade in the new word.
  7. Set the width of the container back to auto. (Again, this should result in no change, because the new word should perfectly fit inside its container)

So while this system seems like it would work perfectly, I was running into a bug where despite calculating the size of the new and old word and animating properly, the containers of size newWidth and oldWidth didn’t quite fit the new and old words (sometimes too big, sometimes too small). This was really aggravating, because I used JavaScript to determine the width of the elements atnewWidth and auto, and they were the same!

$word.width('auto');                  // Word looks good
var oldAutoWidth = $word.width();
$word.width(oldAutoWidth);            // Word doesn't fit in container
var oldWidth = $word.width()
console.log(oldWidth == oldAutoWidth) // True

How could it be the same size yet still appear different? I took to the handy chrome inspector, looking for anything that might be different, and I found it! It turns out that the computed value of the element’s width after changing it’s style differed by a fraction of a pixel.

So you might be thinking “Hey wait, how could a computer monitor display a fraction of a pixel?”. It’s true, it can’t. Although each pixel in a montior can be set to only one value at a time, web browsers work at an abstraction level away from the discrete pixels of screens. If the browser calculates that text takes up 89.224 pixels and that text is placed in a container of width 89 pixels, it will push the last character onto the next line. I tried every way to find the width of an element that I could think of, but every method returned an integer. Aggravating!

The solution, it turns out, was to use Window.getComputedStyle(), which until this point I didn’t even know existed:

$word.width('auto');                  // Word looks good
var oldWidth = window.getComputedStyle($word, 'width');
$word.width(oldWidth);                // Word looks good

This returns a string like “89.224px”, which I could then parse and modify if needed. Once I started using Window.getComputedStyle() in my calculations, all my animations became perfectly smooth, on every browser I tested. (That includes Chrome for Android, Chrome for iOS, Safari Mobile, and Chrome, Firefox, and Safari on desktop.) It was a pretty crazy solution, and I kind of freaked out when I got it working.

So I’m a pro at working with fractional pixels now, I guess. What’s next?