How to be notified once a web font has loaded

JavascriptWebfonts

Javascript 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. The fontSpec 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:

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDaniel WagnerView Question on Stackoverflow
Solution 1 - JavascriptDan DascalescuView Answer on Stackoverflow
Solution 2 - JavascriptThomas BachemView Answer on Stackoverflow
Solution 3 - JavascriptbpierreView Answer on Stackoverflow
Solution 4 - Javascriptfisch2View Answer on Stackoverflow
Solution 5 - JavascriptShaun HareView Answer on Stackoverflow