36

I am currently building a corporate website for a customer that uses custom fonts extensively.

On jQuerys DOM-ready I am doing placement calculations to figure out where some pop-up menus with dynamic width and height should be placed based on their dynamic contents.

These calculations fail, since DOM-ready is fired before font-face is applied, and thus widths and heights are incorrect.

Right now (for the prototype) i am doing the calculations 500ms after DOM-ready to alleviate this problem, but this can't go into production for obvious reasons.

The problem has been observed in latest Firefox and chrome. IE 8 doesn't seem to have the problem, but then DOM-ready fires fairly late, so the delay is kind of built in I guess :)

Waiting for the load event is not an option, so my question to you is this:

Is there a reliable cross-browser way to detect when font-face has been applied?

Martin Jespersen
  • 25,743
  • 8
  • 56
  • 68
  • Well, I'm not sure if this would help but instead of doing your magic on document.ready try to do it on window.load. It fires a bit later when images, etc are loaded, but there might be some font-flickering, which seems undesirable. – ddinchev Jul 13 '11 at 10:25
  • As I already stated, Waiting for the load event is not an option. – Martin Jespersen Jul 13 '11 at 10:26
  • OK, then what do you use to apply the font-face? There are libraries to do this kind of things that might have proper event fired on the right place already. Cufon, FLIR and SIFR are few that I recall that are exactly meant to deal with font-replacement and I would be amazed if they haven't managed to fix the above issue! – ddinchev Jul 13 '11 at 10:32
  • The custom fonts are applied via css using @font-face for definition (fontspring syntax) and font-family declarations. Javascript is not part of it. – Martin Jespersen Jul 13 '11 at 11:27
  • JavaScript solution that tests font string width to determine if font is visible yet: http://stackoverflow.com/questions/12312323/how-to-know-if-a-font-font-face-has-already-been-loaded/12316349#12316349 – Patrick Oct 17 '12 at 03:59
  • Also with ES6 features, there are some new solutions, please see [this answer](https://stackoverflow.com/a/60516253/6877799) – AmerllicA Mar 03 '20 at 22:09

3 Answers3

57

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.

Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
Martin Jespersen
  • 25,743
  • 8
  • 56
  • 68
  • 1
    +1. Hmm. Very interesting, and an excellent bit of research. Thank you for sharing it. I will have to remember this one. (btw, I sense a 'Self Learner' badge in your near future.... ;-)) – Spudley Jul 13 '11 at 12:45
  • 6
    Excellent answer, however I found that Chrome (16.0.912.77) will report that the readystate is 'complete' even if the font-face has not been applied. More accurately if it has been applied it will not necessarily be available if you're trying to use it in something like a Canvas context. Also, a trap I just fell in to, jQuery will not wait to fire its ready event until 'complete', it will also fire if the state is 'interactive' – tapi Feb 02 '12 at 20:28
  • @tapi: jQuerys ready event listens to DOMContentLoaded so it is useless when used with custom fonts. – Martin Jespersen Feb 02 '12 at 22:38
10

ES6 update:

The question post is for many years ago which the IEs version 8 and earlier were still alive and even Ecmascript version 6 was not released, but now you can write callbacks on document.fonts events. eg:

document.fonts.onloadingdone = () => {
  // do something after all fonts are loaded
};

For more information see this post.

Community
  • 1
  • 1
AmerllicA
  • 29,059
  • 15
  • 130
  • 154
0

Setting the function to trigger after a timeout of 200ms solves this issue when using Google Fonts.

There's a noticeable jump, but there usually is for equal heights stuff, for the purists this might not be perfect but it works cross browser.

chim
  • 8,407
  • 3
  • 52
  • 60