onfontready icon indicating copy to clipboard operation
onfontready copied to clipboard

Still Recommended? And document.fonts.load()

Open tolmasky opened this issue 6 years ago • 5 comments

I'm looking for a good library to fallback from when document.fonts.load isn't present in the browser. My searching has brought me here and I am considering it over FontFaceObserver. I see that it hasn't been updated since 2016 though, but that may be because newer browsers haven't required drastically newer techniques. My question is whether you would still use this library for older browsers and whether you plan to eventually use a document.fonts.load technique when available (instead of ResizeObservers mentioned in the docs).

Thanks!

Francisco

tolmasky avatar May 24 '18 18:05 tolmasky

@tolmasky Thanks for reaching out. Neither FontFaceObserver, nor the built-in CSS Font Loading API can handle all the cases that onfontready can, so if you need to detect aspects of font existence that don't involve loading those fonts, this is still the way to go.

As for the general case of handling font-loading, my general recommendation is to use font-display where possible, and fall back to either nothing (since it's a progressive enhancement) or a library such as mine or FontFaceObserver.

I have learned several things about fonts in the last year that will influence both the documentation and code for my library, I just have to get around to it.

Plans include:

  • Additional methods of reducing text jumping when swapping fonts
  • Additional compatibility with newer browsers (particularly Safari)
  • Implementation of ResizeObservers for newer browsers.
  • Since IE9 usage is now below 0.2% worldwide usage, I feel more comfortable setting the cutoff between legacy and modern forms at IE10, meaning that the memory-inefficient iframe method can be entirely dropped and replaced with the scroll event method which is faster, uses far less memory, and doesn't have potential race conditions to mitigate
  • And continued efforts to make it smaller

At this time, I do not intend to ever use the document.fonts.load method, because it is a method for loading fonts, not detecting when fonts are available for use (without triggering a load of said font). If I am mistaken, please let me know, but I'm not aware of a way to use any browser capabilities to natively detect IF a font is loaded and renderable prior to the call to such a method.

dwighthouse avatar May 24 '18 23:05 dwighthouse

Hi dwighthouse, thanks for the quick response!

Regarding fonts.load, this may be a case of conflicting terminology, but I currently use it successfully to detect when a font is "ready" or "safe" to use. If you use it like document.fonts.load("normal 300 Roboto"), then it will succeed if there exists a font definition for Roboto somewhere, but will fail if there isn't, it is thus not itself responsible for "loading". My apologies if you are already familiar with this information, but just to clarify my understanding regarding the strange naming scheme:

  1. Merely including a font (such as with Google Fonts through a link tag) does not actually necessarily "load" the font until it is actually used somewhere on the page (this is true of at least certain browsers, and I think it is subject to whether the font is cached too, can't remember exactly).
  2. Thus document.fonts.check() is not sufficient to see if a font is loaded in tandem with a stylesheet inclusion without separately referencing it somewhere.
  3. This is the reason document.fonts.load does "double duty" in that it both marks the font as "wanted", and then further tells you when it is "ready".

The main point I am attempting to make is that it does not "load" the font in the sense that it is only meant to be used with e.g. url() references, but it is more appropriate to say that it marks it as needed. Thus I believe that, despite its name, document.fonts.load is equivalent to any other font observing method used in the library, as all other methods require referencing the font, and thus also trigger a "load". Hopefully that makes sense.

This happens to be the behavior I desire: I add a Google font tag programmatically, then wish to know when the fonts are ready to go so i can start using them.

tolmasky avatar May 25 '18 01:05 tolmasky

@tolmasky Ah, I thought you were referring to the first part of the Font Loading API which takes a URL. This might indeed be a good way to reduce the library down to nothing for very modern browsers. Certainly, it could represent the most efficient possible way to detect the font, since it doesn't require rendering it in any form.

However, I ran into what might be a bug or inconsistency with some parts.

Check out my JSFiddle Example to see what I mean.

For browser or OS-defined fonts, and for generic font names, the promise resolved callback does not return a reference to the FontFace. I don't yet know if this would actually matter. I'll have to test it on a spectrum of browsers.

In my brief reading of the spec, I don't see what the expected behavior should be for this situation.

dwighthouse avatar May 25 '18 04:05 dwighthouse

I think it would actually be an issue, since fontFaces.lenght === 0 is actually how it tells you its not loaded yet (if the user hasn't yet attempted to load it yet). Here is the code we currently use to demonstrate this:

module.exports = function ({ window, font, timeout = 3000 })
{
    return new Promise(function (resolve, reject)
    {
        const style = font.style || "normal";
        const stretch = FONT_STRETCH ? font.stretch || "normal" : "";
        const css = `${style} ${font.weight} ${stretch} 100px "${font.family}"`;
        const start = Date.now();
        const fonts = window.document.fonts;

        (function check(loaded)
        {
            if (loaded.length > 0)
                return resolve();

            if (Date.now() - start >= timeout)
                return reject();

            window.requestAnimationFrame(() =>
                fonts.load(css).then(check, reject));
        })([]);
    });
}

So, what is accounted for here is when the font observing begins before the font is attempted to be included (here it actually returns loaded.length === 0, not a reject(), a reject() only happens on an actual error). Accepting === 0 as a success state would incorrectly return true in this case.

EDIT: we include the window a parameter here because we load fonts in different windows.

tolmasky avatar May 25 '18 04:05 tolmasky

Yep, document.fonts.load can apparently ONLY detect when a font is loaded successfully when that font is loaded via some kind of URL. It cannot detect the existence or readiness of generic or OS-installed fonts, it seems.

Check out my updated JSFiddle which demonstrates

Thus, document.fonts.load is not a replacement for my library.

Thanks for letting me know about this. It might yet prove useful in some scenarios, but it can't detect whole classes of fonts.

dwighthouse avatar May 25 '18 04:05 dwighthouse