Using Jquery to Know When @Font-Face Fonts Are Loaded

Using jQuery to know when @font-face fonts are loaded?

Ok, it was pretty easy. Basically I just set my text to:

a.main {visibility: hidden;}

and then add:

$(window).bind("load", function() {
$('#nav a.main').addClass('shown');
});

Then make sure that the following is also in my css file:

a.main.shown {visibility: visible;}

How to know if a font (@font-face) has already been loaded?

Now on GitHub: https://github.com/patrickmarabeas/jQuery-FontSpy.js

Essentially the method works by comparing the width of a string in two different fonts. We are using Comic Sans as the font to test against, because it is the most different of the web safe fonts and hopefully different enough to any custom font you will be using. Additionally we are using a very large font-size so even small differences will be apparent. When the width of the Comic Sans string has been calculated, the font-family is changed to your custom font, with a fallback to Comic Sans. When checked, if the string element width is the same, the fallback font of Comic Sans is still in use. If not, your font should be operational.

I rewrote the method of font load detection into a jQuery plugin designed to give the developer the ability to style elements based upon whether the font has been loaded or not. A fail safe timer has been added so the user isn’t left without content if the custom font fails to load. That’s just bad usability.

I have also added greater control over what happens during font loading and on fail with the inclusion of classes addition and removal. You can now do whatever you like to the font. I would only recommend modifying the fonts size, line spacing, etc to get your fall back font as close to the custom as possible so your layout stays intact, and users get an expected experience.

Here's a demo: http://patrickmarabeas.github.io/jQuery-FontSpy.js

Throw the following into a .js file and reference it.

(function($) {

$.fontSpy = function( element, conf ) {
var $element = $(element);
var defaults = {
font: $element.css("font-family"),
onLoad: '',
onFail: '',
testFont: 'Comic Sans MS',
testString: 'QW@HhsXJ',
delay: 50,
timeOut: 2500
};
var config = $.extend( defaults, conf );
var tester = document.createElement('span');
tester.style.position = 'absolute';
tester.style.top = '-9999px';
tester.style.left = '-9999px';
tester.style.visibility = 'hidden';
tester.style.fontFamily = config.testFont;
tester.style.fontSize = '250px';
tester.innerHTML = config.testString;
document.body.appendChild(tester);
var fallbackFontWidth = tester.offsetWidth;
tester.style.fontFamily = config.font + ',' + config.testFont;
function checkFont() {
var loadedFontWidth = tester.offsetWidth;
if (fallbackFontWidth === loadedFontWidth){
if(config.timeOut < 0) {
$element.removeClass(config.onLoad);
$element.addClass(config.onFail);
console.log('failure');
}
else {
$element.addClass(config.onLoad);
setTimeout(checkFont, config.delay);
config.timeOut = config.timeOut - config.delay;
}
}
else {
$element.removeClass(config.onLoad);
}
}
checkFont();
};

$.fn.fontSpy = function(config) {
return this.each(function() {
if (undefined == $(this).data('fontSpy')) {
var plugin = new $.fontSpy(this, config);
$(this).data('fontSpy', plugin);
}
});
};

})(jQuery);

Apply it to your project

.bannerTextChecked {
font-family: "Lobster";
/* don't specify fallback font here, do this in onFail class */
}

$(document).ready(function() {

$('.bannerTextChecked').fontSpy({
onLoad: 'hideMe',
onFail: 'fontFail anotherClass'
});

});

Remove that FOUC!

.hideMe {
visibility: hidden !important;
}

.fontFail {
visibility: visible !important;
/* fall back font */
/* necessary styling so fallback font doesn't break your layout */
}

EDIT: FontAwesome compatibility removed as it didn't work properly and ran into issues with different versions. A hacky fix can be found here: https://github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1

How to know when font-face has been applied

I found a solution after wondering why IE doesn't suffer from this problem.

Firefox and Chrome/Safari triggers the DOMContentLoaded event before font-face is applied, thus causing the problem.

The solution is to not listen for DOMContentLoaded but instead go oldschool and listen to onreadystatechange and wait until the document.readyState === 'complete' which is always triggered after font-face is applied (as far as I can tell by my tests) - which is of course what always happens in IE since it doesn't support DOMContentLoaded.

So basically you can roll-your-own event in jQuery called fontfaceapplied - maybe it should be built in ;)

document.onreadystatechange = function() {
if (document.readyState === 'complete')
$(document).trigger('fontfaceapplied');
};

Funny fact: Opera does it right and waits to trigger DOMContentLoaded until font-face is applied.

detecting in-use @font-face via javascript

Find all css-defined fonts, demo here (console).

function getFonts (obj) {
var o = obj || {},
sheet = document.styleSheets,
rule = null,
i = sheet.length, j;
while( 0 <= --i ){
rule = sheet[i].rules || sheet[i].cssRules || [];
j = rule.length;
while( 0 <= --j ){
if( rule[j].constructor.name === 'CSSFontFaceRule' ){ // rule[j].slice(0, 10).toLowerCase() === '@font-face'
o[ rule[j].style.fontFamily ] = rule[j].style.src;
};
}
}
return o;
}

jQuery misalignment when using font-face

You need to set your desired font before you call outerWidth() to get the width of the link, since changing the font changes the width.

You can do this by moving that part of the .css() call above the outerWidth() call, but it's probably a better idea to just not change the font with Javascript at all, and just set the font you want in your CSS.

The issue you're having with @font-face fonts sounds like the browser hasn't yet loaded the font when the jQuery gets run. Try changing your jQuery code to run on $(window).load instead of $(document).ready to make sure the browser has finished loading everything before the script runs.

jQuery as @font-face initiator

As long as there is no jQuery in the network requests I assume the jQuery is a part of some Chrome extension which applies some fonts / intercepts your fonts calls.

To fix the problem try to disable all extensions you have and try to open the page again.

With the extensions disabled, the initiator reverts back to the original files

Sample Image

Preloading @font-face fonts?

This answer is no longer up to date

Please refer to this updated answer: https://stackoverflow.com/a/46830425/4031815



Deprecated answer

I'm not aware of any current technique to avoid the flicker as the font loads, however you can minimize it by sending proper cache headers for your font and making sure that that request goes through as quickly as possible.



Related Topics



Leave a reply



Submit