How to be notified once a web font has loaded
JavascriptWebfontsJavascript Problem Overview
Google's Web Fonts API offers a way to define callback functions to be executed if a font has finished loading, or couldn't be loaded etc. Is there a way to achieve something similar using CSS3 web fonts (@font-face)?
Javascript Solutions
Solution 1 - Javascript
2015 Update
Chrome 35+ and Firefox 41+ implement the CSS font loading API (MDN, W3C). Call document.fonts
to get a FontFaceSet object, which has a few useful APIs for detecting the load status of fonts:
check(fontSpec)
- returns whether all fonts in the given font list have been loaded and are available. ThefontSpec
uses the CSS shorthand syntax for fonts.
Example:document.fonts.check('bold 16px Roboto'); // true or false
document.fonts.ready
- returns a Promise indicating that font loading and layout operations are done.
Example:document.fonts.ready.then(function () { /*... all fonts loaded...*/ });
Here's a snippet showing these APIs, plus document.fonts.onloadingdone
, which offers extra information about the font faces.
alert('Roboto loaded? ' + document.fonts.check('1em Roboto')); // false
document.fonts.ready.then(function () { alert('All fonts in use by visible text have loaded.'); alert('Roboto loaded? ' + document.fonts.check('1em Roboto')); // true });
document.fonts.onloadingdone = function (fontFaceSetEvent) { alert('onloadingdone we have ' + fontFaceSetEvent.fontfaces.length + ' font faces loaded'); };
We need some text using the font, for the font to be loaded. So far one font face was loaded. Let's add some strong text to trigger loading the second one, with weight: 700.
IE 11 doesn't support the API. Look at available polyfills or support libraries if you need to support IE:
- Web Font Loader - developed by Google and Adobe
- FontFaceOnload - lighter, similar approach to Web Font Loader
- FontLoader - polyfill
Solution 2 - Javascript
Tested in Safari, Chrome, Firefox, Opera, IE7, IE8, IE9:
function waitForWebfonts(fonts, callback) {
var loadedFonts = 0;
for(var i = 0, l = fonts.length; i < l; ++i) {
(function(font) {
var node = document.createElement('span');
// Characters that vary significantly among different fonts
node.innerHTML = 'giItT1WQy@!-/#';
// Visible - so we can measure it - but not on the screen
node.style.position = 'absolute';
node.style.left = '-10000px';
node.style.top = '-10000px';
// Large font size makes even subtle changes obvious
node.style.fontSize = '300px';
// Reset any font properties
node.style.fontFamily = 'sans-serif';
node.style.fontVariant = 'normal';
node.style.fontStyle = 'normal';
node.style.fontWeight = 'normal';
node.style.letterSpacing = '0';
document.body.appendChild(node);
// Remember width with no applied web font
var width = node.offsetWidth;
node.style.fontFamily = font;
var interval;
function checkFont() {
// Compare current width with original width
if(node && node.offsetWidth != width) {
++loadedFonts;
node.parentNode.removeChild(node);
node = null;
}
// If all fonts have been loaded
if(loadedFonts >= fonts.length) {
if(interval) {
clearInterval(interval);
}
if(loadedFonts == fonts.length) {
callback();
return true;
}
}
};
if(!checkFont()) {
interval = setInterval(checkFont, 50);
}
})(fonts[i]);
}
};
Use it like:
waitForWebfonts(['MyFont1', 'MyFont2'], function() {
// Will be called as soon as ALL specified fonts are available
});
Solution 3 - Javascript
The JS Library used by Google Web Fonts API (and Typekit) can be used without the service: WebFont Loader.
It defines callbacks for what you ask, and many more.
Solution 4 - Javascript
2017 Update
The JS library FontFaceObserver is definitely the best, most lightweight, cross-browser solution as of 2017. It also exposes a Promise-based .load()
interface.
Solution 5 - Javascript
The window.load event will fire when everything has loaded - that should include fonts So you could use that as the call back. However I don't think you have to is you decide to use the web font loader as
> In addition to the google, typekit, > ascender and monotype options, there > is also a custom module that can load > a stylesheet from any web-font > provider. > > WebFontConfig = { custom: { > families: ['OneFont', 'AnotherFont'], > urls: [ 'http://myotherwebfontprovider.com/stylesheet1.css', > 'http://yetanotherwebfontprovider.com/stylesheet2.css' > ] } };
The library sends the same events regardless of which provider you specify.