Guessing font availability with Javascript

Posted on 13 November 2008 (09:28 PM)

Recently I wanted to use an uncommon font in a website, but the site was too image-rich to use sIFR, (loading time made for an uneasy user experience).

I decided to add the font anyway in my stylesheet, and provide a “lo-fi” experience, for people who didn’t have the font handy.

The lo-fi version however, should mimic the style of the uncommon font as good as possible.

Sometimes, though, that’s easier said than done.

The problem

The font in question was Copperplate Gothic Light. I had chosen the classic Times New Roman as my backup font (playing it safe), but Times in no way mimics Copperplate.

The only similarity between the two fonts is the fact that they’re both Serifs.

In order for me to make Times look like Copperplate, I had to at least display the text in small-caps.

This is where the first obstacle presented itself: using CSS, I cannot specify a different font-variant for my number one choice than for my fall-back font.
Consider this piece of CSS:

  1. h2 {
  2.  
  3. font-size: 13px;
  4. }
  5. Download this code: /code/guessing_font_availability_with_javascript1.txt

If I would specify font-variant: small-caps;, my text would be displayed in small-caps regardless of which font the user sees. In this particular case that’s no big deal, because Copperplate Gothic Light displays in caps anyway, but let’s say Copperplate renders a bit bigger than Times. In that case I would like to be able to specify a larger font-size in the case where Copperplate would be absent.

I decided I needed another styling hook on which to base style rules. In the event of a user not having Copperplate installed, I would use a class on the BODY element of “alt-font”.
The accompanying CSS rules would look like this:

  1. h2 {
  2.  
  3. font-size: 13px;
  4. }
  5. body.alt-font h2 {
  6.  
  7. font-size: 14px;
  8. font-variant: small-caps;
  9. }
  10. Download this code: /code/guessing_font_availability_with_javascript2.txt

But how to decide when to add that class name to the BODY?

An educated guess

I wrote a little Javascript function that does the guesswork for me. The guessFontAvailability function looks like this:

  1. function guessFontAvailability (desiredFont) {
  2. // create an element with the desired font family
  3. var a = document.createElement ('span');
  4. a.appendChild (document.createTextNode ('AAA'));
  5. a.style.fontFamily = desiredFont + ', monospace';
  6. a.style.fontSize = '100px';
  7. a.style.visibility = 'hidden';
  8. // create an element which will use the browser's default monospace font
  9. var b = document.createElement ('span');
  10. b.appendChild (document.createTextNode ('AAA'));
  11. b.style.fontFamily = 'monospace';
  12. b.style.fontSize = '100px';
  13. b.style.visibility = 'hidden';
  14. // attach the elements to the body
  15. document.body.appendChild (a);
  16. document.body.appendChild (b);
  17. // check if the dimensions of the elements are similar
  18. var available = true;
  19. if (a.offsetWidth === b.offsetWidth &&
  20. a.offsetHeight === b.offsetHeight) {
  21. available = false;
  22. }
  23. // remove the elements and return the availability of the font
  24. a.parentNode.removeChild (a);
  25. b.parentNode.removeChild (b);
  26. return available;
  27. }
  28. Download this code: /code/guessing_font_availability_with_javascript3.txt

This function returns a simple true or false based on a guess about a font’s availability. It works like this:

  1.  
  2. // if the font is not available, provide an alternative styling hook
  3.  
  4. }
  5. Download this code: /code/guessing_font_availability_with_javascript4.txt

How does it work? I’ll walk you through it:

  1. var a = document.createElement ('span');
  2. a.appendChild (document.createTextNode ('AAA'));
  3. a.style.fontFamily = desiredFont + ', monospace';
  4. a.style.fontSize = '100px';
  5. a.style.visibility = 'hidden';
  6. Download this code: /code/guessing_font_availability_with_javascript5.txt

First, a SPAN element is created, containing some text that’ll be rendered in either the desired font, or the browser’s default monospace font.

  1. var b = document.createElement ('span');
  2. b.appendChild (document.createTextNode ('AAA'));
  3. b.style.fontFamily = 'monospace';
  4. b.style.fontSize = '100px';
  5. b.style.visibility = 'hidden';
  6. Download this code: /code/guessing_font_availability_with_javascript6.txt

Second, I create another SPAN element, which renders in the browser’s default monospace font regardless of the desired font’s availibility.

Note that on both elements the visibility property is set to hidden, so users will never notice my little guess.

  1. document.body.appendChild (a);
  2. document.body.appendChild (b);
  3. // check if the dimensions of the elements are similar
  4. var available = true;
  5. if (a.offsetWidth === b.offsetWidth &&
  6. a.offsetHeight === b.offsetHeight) {
  7. available = false;
  8. }
  9. Download this code: /code/guessing_font_availability_with_javascript7.txt

After adding both SPAN elements to the BODY, I check their dimensions. If both the width and the height are the same, the elements are probably both rendered in the browser’s default monospace font. If not, they must be different and therefore there’s a good chance the desired font is available.

Last but not least, I remove the elements from the DOM again:

  1. a.parentNode.removeChild (a);
  2. b.parentNode.removeChild (b);
  3. return available;
  4. Download this code: /code/guessing_font_availability_with_javascript8.txt

Caution!

Please note that the script only guesses. If your desired font accidentally has the same dimensions as the browser’s default monospace font, the function will think the font is not available. But this is probably something you should think about on a per-case basis.

Do you think this script is useful or rubbish? Let me know in the comments! Also, if you know of any improvements, leave a comment and I might write a second version.

Back to top

Filed under HTML and CSS, Javascript

Comments:

  1. 19 November 2008 (10:43 PM) by Dan

    Very Interesting - After reading the title I guessed the approach youn would take - I suppose it's the only way though.

    It would be cool to set up a test page that compares various fonts so you could see how many (if any) common ones provide false results.

  2. 22 March 2009 (12:12 PM) by NetHawk

    Clever approach, I never thought of that.

    I went another way to determine the font availability, but this requires Flash. In Flash you have access to the list of the installed fonts on a client system. So a tiny piece of Flash code retrieves this information (an array) and passes it to JavaScript, where you can do whatever you want with it.

    The best solution would probably be a combination of the Flash approach and yours. If Flash is available (any old version will do), then we're fine. If no Flash is available, your trick comes into play.

    this comment has been quoted by Harmen Janssen

  3. 22 March 2009 (03:54 PM) by Harmen Janssen

    NetHawk wrote:

    Clever approach, I never thought of that.

    I went another way to determine the font availability, but this requires Flash. In Flash you have access to the list of the installed fonts on a client system. So a tiny piece of Flash code retrieves this information (an array) and passes it to JavaScript, where you can do whatever you want with it.

    The best solution would probably be a combination of the Flash approach and yours. If Flash is available (any old version will do), then we're fine. If no Flash is available, your trick comes into play.

    Yeah, you're right, thats actually a more reliable way of doing this. Good thinking :)

  4. 09 April 2009 (08:28 PM) by Frederick Doering

    It's possible to decrease the false negatives with a slight modification.

    First test to see if Arial is available using the current method

    If it's not available then just use the current method

    If it is available, then test to see if the following are of identical sizes:

    a.style.fontFamily = desiredFont + ', monospace';

    and

    a.style.fontFamily = desiredFont + ', Arial';

    if they are identical then your font is 100% certain to be available, if the are different then you're 100% certain that your font is unavailable.

  5. 24 April 2010 (07:13 AM) by trlkly

    @Frederick: If you're going to check a font, why not use one of the guaranteed available font-families, like serif or sans-serif?

    The only failure would be is the one you picked was the same font as monospace, in which case you'd have to fall back to the other method(s).

  6. 18 February 2011 (03:09 AM) by yiwu market

    Perfect!Just keep your work!Thanks for your nice sharing!Just keep on.

Sorry, due to spam, comments are temporarily out of order.

Back to top

Preferences

These settings will be saved for your convenience.