Coder Social home page Coder Social logo

bramstein / fontfaceobserver Goto Github PK

View Code? Open in Web Editor NEW
4.3K 80.0 270.0 693 KB

Webfont loading. Simple, small, and efficient.

Home Page: https://fontfaceobserver.com

License: BSD 2-Clause "Simplified" License

JavaScript 91.01% HTML 8.64% CSS 0.35%
webfonts fontfaceobserver font-loading web-fonts

fontfaceobserver's Introduction

Font Face Observer Build Status

Font Face Observer is a small @font-face loader and monitor (3.5KB minified and 1.3KB gzipped) compatible with any webfont service. It will monitor when a webfont is loaded and notify you. It does not limit you in any way in where, when, or how you load your webfonts. Unlike the Web Font Loader Font Face Observer uses scroll events to detect font loads efficiently and with minimum overhead.

How to use

Include your @font-face rules as usual. Fonts can be supplied by either a font service such as Google Fonts, Typekit, and Webtype or be self-hosted. You can set up monitoring for a single font family at a time:

var font = new FontFaceObserver('My Family', {
  weight: 400
});

font.load().then(function () {
  console.log('Font is available');
}, function () {
  console.log('Font is not available');
});

The FontFaceObserver constructor takes two arguments: the font-family name (required) and an object describing the variation (optional). The object can contain weight, style, and stretch properties. If a property is not present it will default to normal. To start loading the font, call the load method. It'll immediately return a new Promise that resolves when the font is loaded and rejected when the font fails to load.

If your font doesn't contain at least the latin "BESbwy" characters you must pass a custom test string to the load method.

var font = new FontFaceObserver('My Family');

font.load('**').then(function () {
  console.log('Font is available');
}, function () {
  console.log('Font is not available');
});

The default timeout for giving up on font loading is 3 seconds. You can increase or decrease this by passing a number of milliseconds as the second parameter to the load method.

var font = new FontFaceObserver('My Family');

font.load(null, 5000).then(function () {
  console.log('Font is available');
}, function () {
  console.log('Font is not available after waiting 5 seconds');
});

Multiple fonts can be loaded by creating a FontFaceObserver instance for each.

var fontA = new FontFaceObserver('Family A');
var fontB = new FontFaceObserver('Family B');

fontA.load().then(function () {
  console.log('Family A is available');
});

fontB.load().then(function () {
  console.log('Family B is available');
});

You may also load both at the same time, rather than loading each individually.

var fontA = new FontFaceObserver('Family A');
var fontB = new FontFaceObserver('Family B');

Promise.all([fontA.load(), fontB.load()]).then(function () {
  console.log('Family A & B have loaded');
});

If you are working with a large number of fonts, you may decide to create FontFaceObserver instances dynamically:

// An example collection of font data with additional metadata,
// in this case “color.”
var exampleFontData = {
  'Family A': { weight: 400, color: 'red' },
  'Family B': { weight: 400, color: 'orange' },
  'Family C': { weight: 900, color: 'yellow' },
  // Etc.
};

var observers = [];

// Make one observer for each font,
// by iterating over the data we already have
Object.keys(exampleFontData).forEach(function(family) {
  var data = exampleFontData[family];
  var obs = new FontFaceObserver(family, data);
  observers.push(obs.load());
});

Promise.all(observers)
  .then(function(fonts) {
    fonts.forEach(function(font) {
      console.log(font.family + ' ' + font.weight + ' ' + 'loaded');

      // Map the result of the Promise back to our existing data,
      // to get the other properties we need.
      console.log(exampleFontData[font.family].color);
    });
  })
  .catch(function(err) {
    console.warn('Some critical font are not available:', err);
  });

The following example emulates FOUT with Font Face Observer for My Family.

var font = new FontFaceObserver('My Family');

font.load().then(function () {
  document.documentElement.className += " fonts-loaded";
});
.fonts-loaded {
  body {
    font-family: My Family, sans-serif;
  }
}

Installation

If you're using npm you can install Font Face Observer as a dependency:

$ npm install fontfaceobserver

You can then require fontfaceobserver as a CommonJS (Browserify) module:

var FontFaceObserver = require('fontfaceobserver');

var font = new FontFaceObserver('My Family');

font.load().then(function () {
  console.log('My Family has loaded');
});

If you're not using npm, grab fontfaceobserver.js or fontfaceobserver.standalone.js (see below) and include it in your project. It'll export a global FontFaceObserver that you can use to create new instances.

Font Face Observer uses Promises in its API, so for browsers that do not support promises you'll need to include a polyfill. If you use your own Promise polyfill you just need to include fontfaceobserver.standalone.js in your project. If you do not have an existing Promise polyfill you should use fontfaceobserver.js which includes a small Promise polyfill. Using the Promise polyfill adds roughly 1.4KB (500 bytes gzipped) to the file size.

Browser support

FontFaceObserver has been tested and works on the following browsers:

  • Chrome (desktop & Android)
  • Firefox
  • Opera
  • Safari (desktop & iOS)
  • IE8+
  • Android WebKit

License

Font Face Observer is licensed under the BSD License. Copyright 2014-2017 Bram Stein. All rights reserved.

fontfaceobserver's People

Contributors

bramstein avatar codeclown avatar dandaka avatar davidcornu avatar dependabot[bot] avatar fangel avatar iamcarrico avatar kennethormandy avatar themoonrat avatar ttamminen avatar xhmikosr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fontfaceobserver's Issues

Complete Font Failure on Safari

I'm redesigning my website for mach speed. One of the changes is to have readable text asap, hence your library and following Filament Group's style of switching custom fonts on globally once they are detected as parsed: https://www.filamentgroup.com/lab/font-events.html

I have not yet implemented the server side rewrite to set the html class name fontsLoaded upon the detection of a cookie, I'm just at the point of showing the fallback and then jumping to the custom font when ready.

On Safari (OS X Yosemite 10.10.5, Safari Version 8.0.8), with empty cache or not, run as local or from a remote server, I'm getting full font failure in several distinct ways while using the fontfaceobserver library. I have also observed these effects on Safari for iOS (v9, I believe).

Behavior Types

I enumerate the different kinds of weird behavior here first. Please note that in spite of all these behaviors, the Safari Inspector correctly reports style values, and a console log statement ensures that fontfaceobserver is actually running.

  • Font Disappears - The place where the custom font (or even the fallback font) should display is simply invisible; areas that show the custom font when hovered have their text cut off
  • Fallback Displayed - The place where the custom font should display (after having loaded) still shows the fallback font; areas that show the custom font when hovered have their text cut off
  • Correct Behavior - Fallback font is displayed, but quickly replaced with the custom font (as soon as it is detected as loaded by fontfaceobserver)
  • Immediate Custom Display - The custom font displays immediately, without showing the fallback font, even for a moment

Scenarios

There are several different ways of interacting with the loading of the browser and fonts.

  • When first loading the page, I have observed three types of behavior: Font Disappears, Fallback Displayed, and Correct Behavior.
  • When going up to the address bar, highlighting the url, and pressing enter, I always get Correct Behavior.
  • When reloading the page with keyboard shortcuts or the reload button in the address bar, I always get Fallback Displayed behavior.
  • In the Font Disappears and Fallback Displayed behavior situations, if I resize the page in any way, the correct custom font appears.
  • When the custom font is being displayed, if I leave for another website, returning via the back button results in Immediate Custom Display behavior.
  • In the Fallback Displayed behavior situation, if I leave for another website, returning via the back button results in Font Disappears behavior (usually).
  • In the Font Disappears behavior situation, if I leave for another website, returning via the back button results in Font Disappears behavior.
  • In the Font Disappears behavior situation, if I reload the page, I always get Fallback Displayed behavior.

I've also seen one case where reloading caused a flash of the fallback font followed by the fonts disappearing. I've had other slight differences throughout my testing. Basically, few of the above are very consistent. I suspect some kind of race condition is going on in Safari.

My Code

This is the abbreviated code relevant to the problem. Note that I have already tried without the src: local hack, with and without using quotes around my font url references, and tried both font-weight normal and font-weight 300. The font family name should be unique here (doesn't match any font names on my system). I have also tried explicitly setting the non-loaded styles to a font-family of sans-serif.

CSS

@font-face {
    font-family: 'comfortaa-thin-custom';
    src: url(theme/fonts/comfortaa-thin.eot);
    src: local('☺︎'),
         url(theme/fonts/comfortaa-thin.eot?#iefix) format('embedded-opentype'),
         url(theme/fonts/comfortaa-thin.woff2) format('woff2'),
         url(theme/fonts/comfortaa-thin.woff) format('woff'),
         url(theme/fonts/comfortaa-thin.ttf) format('truetype'),
         url(theme/fonts/comfortaa-thin.svg#comfortaathin) format('svg');
    font-weight: 300;
    font-style: normal;
}

html, body {
    background: #ddd;
    font-family: sans-serif;
    color: #777;
}

.fontsLoaded .mainTitle, .fontsLoaded .sectionTitle, .fontsLoaded .imageText {
    font-family: 'comfortaa-thin-custom', sans-serif;
}

.mainTitle, .sectionTitle, .imageText {
    font-weight: 300;
}

Javascript

(function(w) {
    (new w.FontFaceObserver('comfortaa-thin-custom')).check(null, 5000).then(function () {
        w.document.documentElement.className += " fontsLoaded";
        console.log('set font');
    }, function () {});
}(this));

I know this is somehow related to fontfaceobserver, because I tried replacing the fontfaceobserver activator code above with the following code, and got the expected behavior:

setTimeout(function() {
    w.document.documentElement.className += " fontsLoaded";
}, 200);

In the above, it doesn't matter how short or long the timeout delay is, I still get the correct behavior (eventually), albeit with the flash of invisible text due to the time to load/parse the font. Also, when arriving at the page using the back button it loads the font immediately, probably not actually reloading the page, but from a cache of the existing page's contents.

Setting the styles to use the custom font in the CSS immediately (without JS), I get the standard flash of invisible text until the font is loaded.

Examples

Here is a version of my website that exhibits this behavior: http://exp.dwighthouse.com/hex/
Here is a video of the problem: http://exp.dwighthouse.com/FontFailureSafari/FontLoadFailureOnSafari.mov

Font Detection When Default Fonts are Styled?

This might be a dumb question but...

We are considering styling system/fallback fonts so that they take up the same visual space as our web fonts, so that when the web fonts finally load there is minimal (or no) page jump. Given that Font Face Observer (and other font-event polyfills) detect loading by measuring two DOM elements for different sizes, will the detection still be accurate if we style our fallback fonts for matching geometry with webfonts?

Firefox Issues

I am seeing some issues in Firefox. (Tested on Firefox 44). I have to put fontfaceobserver on top of everything (inline css, preload link etc ... ) to make it work. Other browsers work fine even if I put fontfaceobserver code below the inline css.

I have also noticed the same in Firefox when I load google fonts using loadCSS.

In both, fonts work on second load (as the fonts get loaded I supposed but fontfaceobserver doesn't run properly to set class to "fonts-loaded").

Non minified version

Hi, thank you for amazing work on this project :) . Please add non minified version of fontfaceobserver for Bower. It would really be helpful for those who are working with requirejs / amd.

iOS 7 JS Error when using `fontfaceobserver.js` v1.4.9

I did use fontfaceobserver.js v1.4.9 and it worked fine but when I tested my page on my iPhone running iOS 7.1 I did get this error in Mobile Safari:

[Error] TypeError: 'undefined' is not an object (evaluating 'w.Promise.all')

When testing with another iPhone running iOS 8, it worked fine.

I was able to make it work in iOS 7 too by using fontfaceobserver.standalone.js v1.4.7 but is it normal ?

What is the difference between the regular and the standalone version ?
I get the idea that there is some extra code in the standalone, but what is its purpose and when should we use which version ?

Thank you for this cool script :)

support of generic font families

Hi,

is there a support planed for generic font families? Actually, if i try to validate one of the generic font families (e.g. "Tahoma") the promise fails. Supporting generic font families would be really awesome. Please let me know, if this feature isn't planed or even useful for your library.

Thanks a lot - at least for this beautiful library, you did a nice work! ;-)

Safari issues with icon fonts

Chrome and Firefox have no problems with icon fonts, but Safari will fail and prevent any fonts called with Font Face Observer from loading.

I am using weathericons which match up with Yahoo weather codes, so it's kinda essential to use a font rather than svg images.

(function( w ){
    // if the class is already set, we're good.
    if( w.document.documentElement.className.indexOf( "fonts-loaded" ) > -1 ){
        return;
    }
    var fontA = new w.FontFaceObserver( "Open Sans", {
        weight: 400
    });
    var fontB = new w.FontFaceObserver( "Open Sans", {
        weight: 600
    });
    var fontC = new w.FontFaceObserver( "Open Sans", {
        weight: 400,
        style: "italic"
    });
        var fontD = new w.FontFaceObserver( "Open Sans", {
        weight: 600,
        style: "italic"
    });
    var fontE = new w.FontFaceObserver( "weathericons" );
    w.Promise
        .all([fontA.check(), fontB.check(), fontC.check(), fontD.check(), fontE.check()])
        .then(function(){
            w.document.documentElement.className += " fonts-loaded";
            console.log('fonts loaded');
        });
}( this ));

Promise should resolve when font installed locally

Hi @bramstein,

As a performance improvement, FontFaceObserver should include a way of testing if a font is installed locally and then resolve the Promise if given the case before using the font loading API.

I've put together a minimal demo to test/reproduce the scenario more easily. It's testing Arial at the moment.
https://jsbin.com/cogipa/edit?html,js,console

Oddly enough it does resolve on Safari OSX 9.0.3.

EDIT: This variation of FontFaceObserver is to live as a parallel version of it with this extra feature.

Thanks

Timeout not working on Chrome and Firefox but working on IE and Safari

Hello,

It seems the timeout is not working on Chrome and Firefox but is working on IE and Safari.
To test I use 6 font and @font-face and different timeout values
Please see below some logs, the NO Font lines is when the timeout is working

Any idea what the issue can be, a bug, a difference in the browser, or just me not understanding the timeout option?

Log for IE or Safari

1456633652186 : LOADING FontFaceObserver for font: Dosis400 TimeOut: 4200
1456633652187 : LOADING FontFaceObserver for font: Dosis700 TimeOut: 4800
1456633652187 : LOADING FontFaceObserver for font: Dosis200 TimeOut: 300
1456633652187 : LOADING FontFaceObserver for font: Dosis500 TimeOut: 500

1456633652187 : LOADING FontFaceObserver for font: Dosis600 TimeOut: 6600
1456633652187 : LOADING FontFaceObserver for font: Dosis800 TimeOut: 5000
1456633654845 : DOMContentLoaded / font: Dosis400 / (DOMContentLoaded-fontLoading): 2659 / (DomLoadStart-fontLoading): 2543
1456633654845 : DOMContentLoaded / font: Dosis700 / (DOMContentLoaded-fontLoading): 2658 / (DomLoadStart-fontLoading): 2542
1456633654845 : DOMContentLoaded / font: Dosis200 / (DOMContentLoaded-fontLoading): 2658 / (DomLoadStart-fontLoading): 2542
1456633654845 : DOMContentLoaded / font: Dosis500 / (DOMContentLoaded-fontLoading): 2658 / (DomLoadStart-fontLoading): 2542
1456633654845 : DOMContentLoaded / font: Dosis600 / (DOMContentLoaded-fontLoading): 2658 / (DomLoadStart-fontLoading): 2542
1456633654845 : DOMContentLoaded / font: Dosis800 / (DOMContentLoaded-fontLoading): 2658 / (DomLoadStart-fontLoading): 2542
1456633654867 : NO Font Dosis200 is NOT available, font request FAILED / TimeOut: 300 / failed-loading in: 2680
1456633654867 : NO Font Dosis500 is NOT available, font request FAILED / TimeOut: 500 / failed-loading in: 2680

1456633654975 : Font Available Dosis400 , class added :wf-Dosis400 ==> font loaded in: 2789
1456633655113 : Font Available Dosis700 , class added :wf-Dosis700 ==> font loaded in: 2926
1456633655116 : Font Available Dosis600 , class added :wf-Dosis600 ==> font loaded in: 2929
1456633655118 : Font Available Dosis800 , class added :wf-Dosis800 ==> font loaded in: 2931
1456633655075 : OnLoadEvent (OnLoadEvent-DomLoadStart(1456633654729): 346


Log for Chrome or Firefox:

1456633716799 : LOADING FontFaceObserver for font: Dosis400 TimeOut: 4200
1456633716802 : LOADING FontFaceObserver for font: Dosis700 TimeOut: 4800
1456633716802 : LOADING FontFaceObserver for font: Dosis200 TimeOut: 300
1456633716802 : LOADING FontFaceObserver for font: Dosis500 TimeOut: 500
1456633716802 : LOADING FontFaceObserver for font: Dosis600 TimeOut: 6600
1456633716802 : LOADING FontFaceObserver for font: Dosis800 TimeOut: 5000
1456633718030 : Font Available Dosis400, class added :wf-Dosis400 ==> font loaded in: 1231
1456633718031 : Font Available Dosis700, class added :wf-Dosis700 ==> font loaded in: 1229
1456633718121 : Font Available Dosis200, class added :wf-Dosis200 ==> font loaded in: 1319
1456633718121 : Font Available Dosis500, class added :wf-Dosis500 ==> font loaded in: 1319

1456633718178 : DOMContentLoaded / font: Dosis400 / (DOMContentLoaded-fontLoading): 1379 / (DomLoadStart-fontLoading): 1336
1456633718178 : DOMContentLoaded / font: Dosis700 / (DOMContentLoaded-fontLoading): 1376 / (DomLoadStart-fontLoading): 1333
1456633718178 : DOMContentLoaded / font: Dosis200 / (DOMContentLoaded-fontLoading): 1376 / (DomLoadStart-fontLoading): 1333
1456633718178 : DOMContentLoaded / font: Dosis500 / (DOMContentLoaded-fontLoading): 1376 / (DomLoadStart-fontLoading): 1333
1456633718178 : DOMContentLoaded / font: Dosis600 / (DOMContentLoaded-fontLoading): 1376 / (DomLoadStart-fontLoading): 1333
1456633718178 : DOMContentLoaded / font: Dosis800 / (DOMContentLoaded-fontLoading): 1376 / (DomLoadStart-fontLoading): 1333
1456633718182 : Font Available * Dosis600 , class added :wf-Dosis600 ==> font loaded in: 1380
1456633718190 : Font Available * Dosis800 *
, class added :wf-Dosis800 ==> font loaded in: 1388
1456633718295 : OnLoadEvent (OnLoadEvent-DomLoadStart(1456633718135): 160

Best,
Pascal

Unactive chrome window + tab bug

On chrome ubuntu v.41 (Firefox is OK) + Chrome 40 (win):

  • open any app from OS
  • open demo (http://master.origin.font-loading.fgtest.com/font-events.html) in new background unactive tab (middle mouse button click OR ctrl+click)
  • fast press alt+tab (to switch to other app, make chrome unactive)
  • wait page loads
  • switch to chrome back, switch to demo tab - check that page with default fonts

anim

Looks like script works uncorrect when both:

  • not active chrome app
  • and not active loading tab (((

It uses scroll event (described in readme) , maybe chrome doesn't fires it?

Broken export in 1.6.1

It looks like you updated the module to now correctly export the object in the canonical node package way. However, this is a breaking change, and the new version number does not reflect this. It should be 2.0.0.

I suggest you address this by adding back the old method of export, adding a console.warn clause when it is in use (rather than the new common.js one) that this method of using the package is now deprecated, and releasing this version as 1.6.2.

FOUT in Safari; what am I doing wrong?

First of all, thank you for FontFaceObserver, I'm using it to good effect on some of my client's sites.

I first implemented FFO, and it worked great in that pages would render with default Serif or Sans Serif fonts, and when my web fonts loaded, it'd attach the .fonts-loaded class to the and they'd then change to the actual styled font.

So far so good.

But then I tried implementing Scott Jehl's suggestion: http://www.filamentgroup.com/lab/font-events.html to set a cookie after the fonts were loaded, and implement some PHP to put the .fonts-loaded class in the tag if the cookie is set. The idea being the fonts are cached client-side, so for subsequent loads, we don't need use the default fonts.

The problem is, I'm still seeing a FOUT when reloading pages in Safari and Chrome (but not in FireFox). According to this article: http://www.paulirish.com/2009/fighting-the-font-face-fout/ there's a micro FOUT "Displaying a cached ttf/otf/woff", which seems unfortunate.

The thing is, when I reload the Filament Group page: http://www.filamentgroup.com/lab/font-events.html I see no FOUT at all. Yet on a client's site: http://www.richardsonscanalhouse.com I do see it when reloading the page.

Am I doing something wrong, or missing something here?

Font load not detected depending on check() test string

I'm having some trouble detecting load of the Interstate Pi fonts from Font Bureau. They have really bizarre character sets with very spotty coverage of the alphabet.

It seems that the load detection is very sensitive to the test string provided to check(). If font doesn't contain some of the test chars, its load tends to not get detected. However I've found that even in many "normal" fonts, if you set the string to something like "abdef" many fonts won't be detected with that either.

Do you have any recommendations for what kinds of test strings are most effective? I assume a mix of widths and perhaps some kerning pairs would usually be effective.

Promise not resolving with font web service

Hi,

This is more of a question than an issue.

I have an example where I'm using a google webfonts as the font service. I'm using the example from the fontfaceobserver.com and my promise seems to be rejecting.

If i try the same example with a local fonts (same one) and change my references in CSS, the promise resolves and I get a console logged.

Am i doing something wrong when it comes to remote font services or is this not supported hence the promise rejecting?

I've uploaded my example to github https://github.com/qasimalyas/fontsobserver

Many thanks

fontfaceobserver is downloading the font even when not used on the page

@font-face is downloading only the fonts used on the page being displayed but when using fontfaceobserver on a font it gets downloaded even if not used on the page.

I believe this is the normal behavior and not and bug/issue and mostly would like to confirm.

It is not a major issue but I am thinking of writing some code to take care of this and run fontfaceobserver only for the fonts used on the page. This would be my approach:

  1. Parse the HTML and search for all classes starting with wf- (assuming that the classes used and set are always wf-fontname
  2. Set a cookie listing the fonts for the specific page: fontlist=wf-font1, wf-font2, etc.
  3. Run fontfaceobserver only for the fonts listed in the cookie

This can be done server side with some PHP (I use Drupal), or in Javascript client side. I think server side is better/ more efficient but there will be some issues with caching with things like varnish (they can be solved but it introduces complexity).

Has anyone had some experience with this or are there other options/ solutions?

Best,
Pascal

Avoid horizontal scrolls

on mobile devices (iPhone6 for ex.) while page loads i see annoying horizontal scroll few seconds.
it brake page layout (((
i know that you are using some technic (any detailed description?) with scroll events, but how it can be fixed?
element that blow column is:

<span style="display: inline-block; position: absolute; height: 100%; width: 100%; overflow: scroll;">
    <span style="display: inline-block; width: 200%; height: 200%;"></span>
</span>

weight not taken into account in success of Promise

Hi

We have an issue and we're thinking it might be a fontfaceobserver bug.

var adelle, adellebold;

adelle = new FontFaceObserver('adelle sans', {
    'weight': 'normal'
});

adellebold = new FontFaceObserver('adelle sans', {
    'weight': 'bold'
});

adelle.check().then(function() {
    htmlElement.className += ' adelle-loaded';
});

adellebold.check().then(function() {
    htmlElement.className += ' adelle-bold-loaded';
});

In the CSS we have the following:

@font-face {
    font-family: 'adelle sans';
    src: url($font-path + 'adellesans-bold-webfont.eot?c=' + $fingerprint);
    src: url($font-path + 'adellesans-bold-webfont.eot?#iefix&c=' + $fingerprint) format('embedded-opentype'),
         url($font-path + 'adellesans-bold-webfont.woff2?c=' + $fingerprint) format('woff2'),
         url($font-path + 'adellesans-bold-webfont.woff?c=' + $fingerprint) format('woff'),
         url($font-path + 'adellesans-bold-webfont.ttf?c=' + $fingerprint) format('truetype'),
         url($font-path + 'adellesans-bold-webfont.svg#adelle_sansbold?c=' + $fingerprint) format('svg');
    font-weight: bold;
    font-style: normal;
    font-stretch: normal;
}

@font-face {
    font-family: 'adelle sans';
    src: url($font-path + 'adellesans-regular-webfont.eot?c=' + $fingerprint);
    src: url($font-path + 'adellesans-regular-webfont.eot?#iefix&c=' + $fingerprint) format('embedded-opentype'),
         url($font-path + 'adellesans-regular-webfont.woff2?c=' + $fingerprint) format('woff2'),
         url($font-path + 'adellesans-regular-webfont.woff?c=' + $fingerprint) format('woff'),
         url($font-path + 'adellesans-regular-webfont.ttf?c=' + $fingerprint) format('truetype'),
         url($font-path + 'adellesans-regular-webfont.svg#adelle_sansregular?c=' + $fingerprint) format('svg');
    font-weight: normal;
    font-style: normal;
    font-stretch: normal;
}

To test if the classes actually work, we removed the first font-face (the bold one).

The expected result is that the promise of adellebold is NOT executed and that there is no adelle-bold-loaded class on the html-element.
However, the actual result is that the promise is executed and therefor the css-class is added.

Is this a bug with fontfaceobeserver or are we using/doing it wrong?

Thanks in advance!

Kind regards,
Yannick

What to do with static sites?

I understand that it's a common solution to set a cookie and apply a class server side to prevent a FOUT when fonts are already loaded. But how would you solve this for static sites?

Right now fontface observer is working very well, but because the html isn't processed server side, I've no way to add the fontsLoaded class before it reaches the client. So currently I'm seeing a FOUT for every page load (for example this site and the matching repo)

Fallback when javascript is disabled

In your article you said:

This will load each web font in­de­pen­dently, which is use­ful when the fonts are un­re­lated and sup­posed to ren­der pro­gres­sively (i.e. as soon as they load). Un­like the na­tive font load­ing API you don’t pass font URLs to Font Face Ob­server. It will use the @font-face rules al­ready avail­able in your CSS to load fonts. This al­lows you to load your web fonts man­u­ally us­ing JavaScript, with a grace­ful degra­da­tion to ba­sic CSS.

What graceful degradation? Even if you add the <link> tags inside <noscript> or just have the @font-face declarations in your CSS it won't work. Because no javascript means no fonts-loaded class in <html>, so the custom fonts will never display.

What i'm missing?

FOUT on slow devices even when fonts have been downloaded

I noticed that there can be an FOUT on slow devices. For example an old smartphone or tablet or when a use Virtualbox with Mac OS X and Safari. The FOUT is happening even when the font has been already downloaded, it is very short but still, it is.

From what I could observe it seems to come from the time it takes fontfaceobserver to apply the class when it detected the font is available, and then for the devices to do the re-render and re-paint. So the solution is to set the class base on a cookie that is when the font has been download (font-x-available=1 cookie). So far so good, this is working.

But there is an issue when a user is refreshing/ reloading the page (F5 or click on the refresh), the cookie saying the font is available is still there, the class gets added but the font needs to be downloaded. The issue is not the FOUT but the fact that the font/text doesn’t have the same width and there is a major re-layout or re-flow happening.

My solution so far is to detect the reload/refresh by looking at HTTP_CACHE_CONTROL on the server side (I could not find how to do this on the client side):

  1. when it is a reload set a “reload=1” cookie (server side).
  2. On the client side, if the cookie reload=1 is set: do not add the font classes that would be added based on the font-x-available cookie, and delete the cookie. This makes the page load as it would be the first ever time following the usual process.

Network speed has no influence on the FOUT on slow devices, the FOUT is happening anyway, but slow network makes the re-paint/ re-flow just worse.

I am aware this is somewhat overdoing it! I was wondering if other people have seen this FOUT + re-paint on slow devices. And if there is another way to take care of the slow device + reload issue.

Best,
Pascal

Improve compression rate

Do you have any plan to improve the compression rate of this fantastic lib?
For instance, by passing window and document vars to the wrapper function they can get minified. Or by using some vars to "cache" some repeated strings or the this keyword, so the compressor can better minify them.

Flash of invisible text

I thought I'd just float this problem in case I'm not the only one who experienced this and someone else finds it in future (Closed or otherwise).

I implemented @scottjehl's suggestion for using this code for eliminating the FOIT caused by CSS parsing. Unfortunately, I needed to roll this back as my FOIT time increased. I didn't really have the time/resources to find the root cause so don't mind if you close this unsolved.

On a fast connection, I'd see perhaps a half second FOIT, and when slowing down using Chrome devtools throttling it could be in the multiple seconds. Here's what I know, in case it helps someone else or someone else can help me:

  1. I was using two fonts
  2. I tried both the standalone (with promises) approach as well as our own Q-based promise
  3. The promises weren't firing immediately - they did seem to be waiting on something.
  4. The promises fired a little before the fonts showed (visually) as finished in dev tools network view. Also it seemed like the time the FOIT turned into visible text did coincide with devtools showing the fonts being present
  5. Problem was present on multiple devices and browsers
  6. I tried using both check() as well as check(null, 10000) and neither made a difference

One suspicion was that this FOIT coincided with the async loading (using loadCSS) of my bootstrap CSS file. The FOIT certainly seemed to happen at around the same time. However I removed all custom font references from it and still the problem persisted.

fontfaceobserver and arabic fonts

I am implementing fontfaceobserver in a website with custom Arabic fonts and doesn't work properly, always call the failure part of the promise.

So, I'm using an Arabic string pasted in the source and a string in Unicode notation as string in the check function . Also tried with ASCII string.

For test, I downloaded the source code of fontfaceobserver and a couple of fonts from https://github.com/misamplus/dinky-theme/tree/master/font and ran the testsuite (modifying the first family font test with an arabic font like BMitra Regular). Console displays: Uncaught (in promise) Error:
expected 301 not to equal 301

Thank you for your help.

Is there an issue with icon fonts ?

I tried to use this library to detect if an icon font is available. (ie: Font Awesome)
The following check always failed, even if the font is properly loaded (I can see the icons):
var fontAwesomeObserver = new FontFaceObserver('FontAwesome', {});

I think this is because icon fonts are using Unicode code points through the content css property, and I did not manage to detect them with this library. I tried with String.fromCodePoint, String.fromCharCode.

These Unicode points are subject to change and it would not be "wise" to rely on them.
Icon fonts use class names to specify icons, do you think we could adapt the library to accept class names as testString ?

BESbswy shows up in Google Web Light

@bramstein

The string BESbswy shows up in Google Web Light. Demo here at the bottom of the page:

It's obviously a Google Web Light bug, but I don't know to whom to report so maybe you can report it to them or I can. Or you may want to change your code if you think it'll take time for them to correct it.

Material Icons font in Safari OS X: issue and fix

Material Icons is a font that uses ligatures to display icons (http://google.github.io/material-design-icons/#icon-font-for-the-web).

Both Chrome and Firefox successfully detect when it's loaded, but on Safari 9.0.3 (OS X 10.11.3, Retina display) checking for it fails (even though the font is actually loaded).

Test case:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <link href='https://fonts.googleapis.com/css?family=Material+Icons|Source+Sans+Pro' rel='stylesheet' type='text/css'>
  </head>
  <body>
<i class="material-icons">favorite</i>
<div id="result">Checking...</div>
<script src="https://rawgit.com/bramstein/fontfaceobserver/master/fontfaceobserver.js"></script>
<script>
var fontObservers = [
    new FontFaceObserver('Material Icons'),
    new FontFaceObserver('Source Sans Pro')
];
var resultElm = document.getElementById('result');

Promise.all(fontObservers.map(function(observer) {
    return observer.check(null, 5000);
})).then(function() {
  resultElm.innerText = 'OK';
}).catch(function() {
  resultElm.innerText = 'Failed to load';
});
</script>
</body>

To fix the issue, I used text that triggers ligature:

observer.check('favorite', 5000);

However default text plus ligature ('BESbswy favorite') doesn't work. The question is: why Chrome and FF don't have problems with it, and can this be somehow fixed in FontFaceObserver itself?

Thank you!

Promise fails on OSX Chrome 42

For some reason I can't get FFO to work on my Chrome 42, OSX.

I've added FFO (standalone) just above </body> and initialize FFO as:

// main.js
(function( win, doc, undefined ){
  var observer = new FontFaceObserver('Open Sans', { weight: 300 } );
  observer.check()
    .then(function(result){
      console.log('Fonts loaded');
      doc.documentElement.className += " fonts-loaded";
    }, function(error){
      console.log('Fonts NOT loaded');
    });
}( this, document ));

This works in Fx, Safari and even my Chrome Mobile 42 on my Nexus4, but not on my Desktop Chrome

The Promise does return a z observer object that looks ok to me:

{
  family: "Open Sans"
  featureSettings: "normal"
  stretch: "stretch"
  style: "normal"
  variant: "normal"
  weight: 300
}

I must be missing something but I can't see why Chrome fails.

Some details:

Reading up on #1 I've considered the scroll-stuff to potentially conflict with Browsersync's scroll-syncing stuff, but that's just a wild guess.

Any thoughts?

body may not be parent element at removal time

Just ran into an issue where this script combined with an offCanvasMenu (hamburger menu) script caused the to NOT be the parent when it came time to remove the child node, so it errors out on

null !== k.parentNode && document.body.removeChild(k)

Changing it to be this seems to fix it for my case but I didn't do any real testing to confirm there weren't other issue at play.

null !== k.parentNode && k.parentNode.removeChild(k)

Prefetch and slow connections causes `Uncaught (in promise)` / promise rejected error

If I prefetch fonts:

<link rel="prefetch" href="fonts/charter-bt-roman.woff2">
<link rel="prefetch" href="fonts/charter-bt-italic.woff2">

And observe them with FFO:

let fontObservers = [
  new FontFaceObserver('Charter BT').check(),
  new FontFaceObserver('Charter BT', {style: 'italic'}).check()
];

Promise.all(fontObservers).then(function() {
  document.documentElement.classList.add('is-fontsLoaded');
});

and slow my connection (regular 2g with Chrome Dev Tools):

Expected outcome: It takes a while but my fonts load.
Actual: I get the error Uncaught (in promise). If I return the all promise, the values are Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: z}.

The odd thing is this works fine if I'm throttling to a slightly faster value, even regular 3g. Let me know if you need any more info, or have ideas.

Error on iPad Air, HTC One ["Error: Can't find variable: Promise\na@..."]

I'm using the plugin in this enviroment:

"angular": "1.2.15",
"jquery": "1.10.2",
"fontfaceobserver": "~1.4.5"

On Desktop: Chrome (41&44), FF, FF Dev works okay
On Mobile: Working okay on Nexus 4 (Chrome), Nexus 7II (Chrome)

Defect detected on iPad Air, HTC One, iPhone4, iPhone 5

Error message (in FF bug lite):

Error: Can't find variable: Promise\na@.."aggregated script line here"

Which point to this line:

z.prototype.a=function(a,b){var c=a||"BESbswy",C=b||3E3,h="font-style:"+this.style+";font-variant:"+this.variant+";font-weight:"+this.weight+";font-stretch:"+this.stretch+";font-feature-settings:"+this.featureSettings+";-moz-font-feature-settings:"+this.featureSettings+";-webkit-font-feature-settings:"+this.featureSettings+";",k=document.createElement("div"),l=new v(c),m=new v(c),n=new v(c),d=-1,e=-1,f=-1,q=-1,r=-1,t=-1,p=this;return new Promise(function(a,b){function c(){null!==k.parentNode&&document.body.removeChild(k)}

Where the context is:

new Promise

And in the aggregated script I can't see Promise promise definition itself.

IE9 Not Working

I am using 1.6.1 of fontfaceobserver.js with the included polyfill.

Works just fine in IE10 & IE11, however, it does not seem to work in IE9:

var fontObservers = [];

var fontFamilies = {
  'Univers LT W01_55 Roman1475956': [
    {
      weight: 400
    }
  ]
}

Object.keys(fontFamilies).forEach(function(family) {
  fontObservers.push(fontFamilies[family].map(function(config) {
    return new FontFaceObserver(family, config).check()
  }));
});

Promise.all(fontObservers)
  .then(function() {
    document.documentElement.classList.add('fonts-loaded');
  }, function() {
    console.info('Web fonts could not be loaded in time. Falling back to system fonts.');
});

I receive no error or message in console whatsoever, nor is the .fonts-loaded class ever added to my html element.

Provide the cache ttl for the font file

I'm currently using a cookie to say when the font has been downloaded. If the cookie could be set to the Expires header from the downloaded font that would be nice.

Promise fails when font is installed locally (known bug in Chrome on Mac OS X)

Hi,
found out that if font is installed locally then promise is rejected.
https://code.google.com/p/googlefontdirectory/issues/detail?id=43
In my case it's not consistent across fonts — I do request PT Sans and PT Sans Caption from Google fonts api and it fails only on PT Sans (if it's installed locally).

Are there any ideas whether this can be fixed within fontfaceobserver?
As an external user of fontfaceobserver that (local presence of the font) should not change behaviour imho, but maybe there's no [decent] way to encapsulate this inside fontfaceobserver.

Thanks for your wonderful library.

RTL languages don't detect properly

When detecting fonts with dir="rtl" at the top level the detection fails. Adding dir="ltr" to the detection div element resolves the problem.

Firefox 24 & lower support

Is Firefox 24 & lower supported? I can't get the FontFaceObserver to resolve with these browsers. Latest FontFaceObserver (v1.7.1) used with your polyfill.

See Demo - works in all browsers but FF 24 & lower:
https://jsfiddle.net/ego84zav/7/

I used browerstack to test with older version of firefox.

Matt

Observer check doesn't work in IE for some fonts (FontAwesome)

Though it hurts my soul to file an IE issue, I'm at a loss to find a fix and am not at liberty to turn my back on IE for this project.

I'm trying to use fontfaceobserver to detect when an icon font (i.e. FontAwesome) is loaded--but it doesn't seem to work on IE browsers. I have created the following fiddle to demonstrate my issue:

https://jsfiddle.net/TheMadDeveloper/15s03g3u/3/

The promise returned by check() resolves properly for most browsers, but is rejected in IE, after the timeout, even though the font loads before then. I believe this is the case for all versions of IE, although IE10 was what I was using when I last tested.

I spent some time trying to trace down the issue, though I was working with the minified version because I clearly enjoy pain. I followed the execution path to: dom.waitForBody(function () { ... }) I got a little confused around var hidden = document['hidden']; (which always seems to === false in IE). Additionally, the onResize() callbacks on the rulers are never called. So, ultimately, the actual check() function defined in that block around line 198 never gets called.

More FYIs:

  • When I tried manually resizing the ruler elements after the page loaded (having prevented their removal), the ruler onResize listeners do get triggered.
  • This might be font specific--I can't remember if it successfully worked for more standard fonts.
  • Technically, I'm trying to make this work for a custom font, but FontAwesome seems to have the same issue.

Page not loading at the top

Hi Bram,

I think I may have came across an issue with FFO on iOS (both Safari & Chrome).

When I start navigating through a website which uses FFO it starts to act funny, in that the website starts loading the pages one-third (roughly) down, rather than the top of the page. This results in the user having to scroll to the top. Just wondering if this is something that you have came across before?

It could well be that I am doing something wrong - but I have implanted FFO into 3 sites now and they are all doing the same. When I remove the FFO script normality resumes.

Examples repo - https://github.com/conormcafee/fearon-bros/blob/gh-pages/build/js/fontFaceObserver.js
Example Site - http://www.fearonbros.co.uk

Provide a non-minified version of the library

Right now, fontfaceobserver.standalone.js and fontfaceobserver.js are minified.

It would be preferable if those were not minified, and separate minified version was provided (i.e. fontfaceobserver.standalone.min.js).

commonjs export in dist file?

Love this script.

I'm in the process of moving our Enhance boilerplate to use a more automated build that pulls in its dependencies from NPM, and this script is one of the four or so dependencies I'll be referencing. Unfortunately, the NPM distribution doesn't appear to have a commonjs module export, but I may have missed something. If not, would that be something worth adding?

Maybe something like this would work?

// commonjs
if( typeof module !== "undefined" ){
    module.exports = FontFaceObserver;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.