More reliable font events

We’re often asked by developers why Typekit uses JavaScript to load web fonts. Of the many advantages, one leading benefit is that JavaScript can monitor the page, detect the various stages of font loading, and provide the means for a developer to respond. This gives the developer the power to consistently control the Flash of Unstyled Text (or FOUT), measure web fonts for dynamic layouts, and more.

Typekit provides these capabilities through font events, which detect the state of fonts and allow developers to work with loading, active, and inactive CSS class names and JavaScript callbacks. While font events are fairly reliable, the WebKit rendering engine has had a long-standing bug which can cause the active event to fire too early. While the bug is fixed in recent versions, many browsers on mobile and desktop still have this problem.

Today we’re happy to announce that we’ve created a workaround which has been incorporated into the open-source webfontloader library as well as all newly-published Typekit kits. To take advantage of this fix in your kits, simply head to typekit.com and republish. To learn more about the bug, our workaround, and the future of web font loading detection, read on.

Font loading detection

Web browsers today don’t have a native API for telling the page when web fonts are loaded and ready for use. We work around this missing functionality by inserting an element into the DOM and retrieving its width and height. Then we add the font we are trying to load to the ‘font-family’ property of the element and set up a timer to periodically check the width and height of the element again. If at any point the width and height are different from the original size, we assume that the font has loaded. If no change is observed within five seconds, we assume the font has failed to load.

An additional complexity is the possibility of a metric compatible font being loaded. A metric compatible font is designed to have identical width and height for each glyph compared to another font. An example of this is the Liberation font family, which was designed to be metric compatible with Arial, Times New Roman, and Courier.

While metric compatible fonts are rare, they will actually cause a false negative in our detection strategy. From the perspective of our element, the width and height never change if the local font and the web font have identical metrics. The font detection code will wait until the timeout and then assume the font has failed to load.

To counteract metric compatible fonts, we introduce another element with a second local font fallback that has different metrics from the one we used before. This means that if the web font loads, both elements have the same size; if it doesn’t load, at least one of the elements will have a different size.

font-measuringA pair of elements used for font detection. The red borders show the boundaries of each span, which is where width and height are measured. Serif and sans-serif fallback fonts are used to counteract metric compatible fonts.

WebKit bug

While this size detection scheme works smoothly in most browsers, a bug in older WebKit versions makes it unreliable. WebKit versions that have this bug will switch to a “last-resort” font while loading web fonts. The last-resort font is the font you get when the browser does not know which font to use. This means it will not use the normal font fallback stack, and instead choose a platform-specific font.

Further complicating matters, on some platforms the browser will also try to choose a last-resort font based on the last item in the font stack that first references the web font. Other platforms may hard-code a default last-resort font, or use a strategy for selecting a last-resort font where selected unicode ranges map to different fonts.

This means that in our DOM element monitoring scheme, we’ll perceive up to two different changes in width and height. If we happen to measure the element while the last-resort font is in use, and the width or height is different, we’ll interpret that the web font has been loaded before it’s actually ready.

Workaround

We work around the bug by maintaining a list of last-resort fonts and measuring the width and height of each. If a change in width and height occurs on the DOM element and the bug is present, we’ll compare the width and height with each entry in our list of last-resort fonts. If a match is found, we know that the size change is probably not due to the web font loading, but rather to the WebKit bug switching the font to the last-resort font.

However, there is a problem with this approach when the web font we’re trying to load is metric compatible with one of the last-resort fonts. In this case, we’ll see the first change in width and height when the last-resort font becomes active, but never a second when the web font is ready.

To mitigate this problem, we’ve added a special case to the timeout. If the bug is present, and the font detection code times out while the width and height match the last-resort width and height, the webfontloader will assume the timeout is due to the last-resort font and web font having identical metrics.

Looking ahead: Native font events

The CSS3 Fonts Module recently introduced a new interface on the document object called fontloader. This interface can be used to explicitly load web fonts, or to notify when a font has loaded (or failed to load). This means that going forward, as browsers more regularly feature built-in support for detecting all the phases of web font loading, our workaround won’t be a required step.

The fontloader interface is pretty straightforward, and it will appear familiar as it was partly influenced by the design of the open-source webfontloader library that Typekit collaborates on and uses in kits. It has three properties called onloadstart, onload and onerror for when a font starts loading, when it finishes loading, and when it has failed to load.

document.fontloader.onloadstart = function (e) {
  console.log(e.fontface + ‘ has started loading’);
};

document.fontloader.onload = function (e) {
  console.log(e.fontface + ‘ has finished loading’);
};

document.fontloader.onerror = function (e) {
  console.log(e.fontface + ‘ has failed to load’);
};

It is also possible to use the fontloader interface to manually trigger loading a font from JavaScript. There is currently no browser support for the fontloader interface, but we’ll update webfontloader to use the native font events as soon as it is implemented in browsers.

9 Responses

  1. philipmeissnerdesign says:

    Can a “Republish All My Font Kits” button be created? Republishing 40+ kits is very time consuming.

    1. Agreed. The root problem here is that TypeKit across the board hasn’t (yet) designed for their users to have many different kits. It’s evident in the lacking of an overview page, in no group republish, & even in the kit dropdown selector (looks goofy once you pass 20 kits; usability suffers when that number continues growing).

      Hope you’re hearing this, TypeKit! :)

    2. Michael Fink says:

      Much of htis goes right over my head, but I’m glad Typekit is looking after me.

      But I also agree with both responses so far – it’s time to, at a minimum, offer a Republish All Kits option.

    3. shakefu says:

      Use the Typekit API to republish large numbers of kits.
      https://typekit.com/docs/api

      We actively maintain about 600,000 kits without any trouble.

    4. Ben W. says:

      We hear you. Definitely a pain when you have more than several kits. We plan to make republishing and general kit management easier and better in the future.

  2. Sebastian says:

    Far better. Thanks.

  3. Ep says:

    Management, in general, is lacking onTypekit.

  4. Any news on those Monotype fonts that were ‘Coming Soon’ nearly 5 months ago?

    1. Bram Stein says:

      Sorry it is taking such a long time. We are working on making the Monotype fonts available and will post more information as soon as we can.

Comments are closed.