UNPKG

foutbgone

Version:

FOUT-B-Gone addresses the flash of unstyled type (FOUT) problem in Firefox 3.5/3.6 and Internet Explorer 7–9.

236 lines (188 loc) 8.29 kB
(function (root, factory) { if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof exports === 'object') { module.exports = factory(); } else { root.FoutBGone = factory(); } }(this, function() { 'use strict'; var FoutBGone = function(opts) { //private vars var self = this; var test_frequency = 20; //how often (in ms) to check if test node has been styled with last custom font in list var giveup = 3000; //number of ms before it stops checking (i.e., custom font style was not applied) var latency = 100; //delay between test node being detected as styled and hidden nodes being exposed //public vars this.rfu = null; //user settings //private method declarations var init = null; var onWinLoad = null; //public method declarations this.hideFOUT = null; this.isFontFaceSupported = null; //public events (callbacks) -- to be defined by client code this.onFontFaceFailed = null; /****************************************************************************/ //private method definitions: init = function() { //rfu }; onWinLoad = function(callback) { if (window.addEventListener) addEventListener('load',callback,false); else attachEvent('onload',callback); }; /****************************************************************************/ //public method definitions: this.hideFOUT = function(when, delay) { //inventories custom fonts used on a page and selectively hides only DOM elements that would cause flash-of-unstyled-text //args: when -- 'asap'|'domready'|'onload' -- when hidden content will revert to visible (optimum varies by page and browser) if (navigator.appName != 'Microsoft Internet Explorer' && !/Firefox\/3/.test(navigator.userAgent)) return; //browser-specific test because IE and Firefox 3.x are those w/ FOUT problem delay = delay || latency; var fontnams = []; var fontruls = []; var selectrs = []; var iscompliant = true; //inventory all custom fonts declared through @font-face rules and make list of all non-in-line css style rules in page: for (var i = 0; i < document.styleSheets.length; i++) { var stylsheet = document.styleSheets[i]; if (!stylsheet.cssRules) { //find @font-face rules "manually" for IE8, IE7, etc. iscompliant = false; var rls = stylsheet.cssText; rls.replace(/@font-face\s*\{([^\}]+)\}/ig, function(r, t){ var fontnam = r.replace(/([\s\S]*)(font-family:\s*['"]?)([-_0-9a-zA-Z]+)([\s\S]*)/, "$3"); fontnams.push(fontnam); return r; }); } var ffrules = stylsheet.cssRules || stylsheet.rules; for (var j = 0; j < ffrules.length; j++) { var rul = ffrules[j]; if (iscompliant && rul instanceof CSSFontFaceRule) { var fontnam = rul.cssText.replace(/([\s\S]*)(font-family:\s*['"]?)([-_0-9a-zA-Z]+)([\s\S]*)/, "$3"); fontnams.push(fontnam); } else fontruls.push(rul); //CSSStyleRule } //alert(fontnams); //alert(fontruls.length); } //make list of all style rules that use a custom font for (var i = 0; i < fontnams.length; i++) { for (var j = 0; j < fontruls.length; j++) { var csstxt = iscompliant ? fontruls[j].cssText : fontruls[j].style.cssText; if (csstxt.indexOf(fontnams[i]) != -1) { selectrs.push(fontruls[j].selectorText); } } } //create a span node to be used for measuring default-font-styled vs custom-font-styled //Note: the span technique is modeled after code developed by Paul Irish (http://paulirish.com/2009/font-face-feature-detection) var body = document.body || document.documentElement; //.appendChild(document.createElement('testhost')); var spn = document.createElement('span'); spn.setAttribute('style','font:99px _,serif;position:absolute;visibility:hidden'); spn.style.visibility = 'hidden'; //fixes ie7 text flashing bug spn.innerHTML = '-------'; spn.id = 'fonttest'; body.appendChild(spn); //var wid = spn.offsetWidth; //moved further down; even though innerHTML was already set above, this is too early for IE //create a new stylesheet to store new classes with visibility:hidden for all nodes with custom fonts var stl1 = document.createElement('style'); document.getElementsByTagName("head")[0].appendChild(stl1); var allhidden = ''; for (var i = 0; i < selectrs.length; i++) allhidden += (selectrs[i] + (i < (selectrs.length - 1) ? ', ' : ' ')); allhidden += '{visibility:hidden}'; if (stl1.styleSheet) stl1.styleSheet.cssText = allhidden; //IE8, IE7 else stl1.textContent = allhidden; //e.g., "h1,div.test{visibility:hidden}"; spn.style.font = '99px "' + fontnams[fontnams.length-1] + '",_,serif'; //apply custom font to test node, e.g., 'URWGroteskT_LigNar' var wid = spn.offsetWidth; //at this time, tests in newer browsers (not IE7/IE8) show this as still the width of the original default-font //alert('before: ' + spn.offsetWidth); //test: shows width for default font //setTimeout(function(){alert('after: ' + spn.offsetWidth);}, 1000); //test: shows width for custom font var temp1 = ''; var freq = test_frequency; var showHidden = function() { var fnttest = setInterval(function() { if (!wid && document.body) //for the benefit of IE7 and IE8 { body.removeChild(spn); document.body.appendChild(spn); wid = spn.offsetWidth; } var nu_wid = spn.offsetWidth; temp1 += (nu_wid + ' '); //temp1 is used for dev only giveup -= freq; if (wid != nu_wid || giveup <= 0) { clearInterval(fnttest); setTimeout(function(){stl1.parentNode.removeChild(stl1);}, delay); //even 'asap' needs a small delay if (giveup <= 0 && self.onFontFaceFailed) self.onFontFaceFailed(); spn.parentNode.removeChild(spn); } }, freq); } //decide when to start testing if custom font has been applied if (when == 'asap') showHidden(); //else if (when == 'domready') head.ready("dom", showHidden); //'domready' requires head.js (temporarily not supported) else if (when == 'onload') onWinLoad(showHidden); else showHidden(); //default is same as ('asap',100) if (window.TESTCAPTURE) //dev testing only (optional) { onWinLoad(function() { document.getElementById('hf_monitor_div').innerHTML = temp1; setTimeout(function(){ document.getElementById('hf_monitor_div').innerHTML += '<br>Final: ' + spn.offsetWidth; }, 1000); }); } }; this.isFontFaceSupported = function() { //The code in this method was written by Diego Perini var sheet, doc = document, head = doc.head || doc.getElementsByTagName('head')[0] || docElement, style = doc.createElement("style"), impl = doc.implementation || { hasFeature: function() { return false; } }; style.type = 'text/css'; head.insertBefore(style, head.firstChild); sheet = style.sheet || style.styleSheet; var supportAtRule = impl.hasFeature('CSS2', '') ? function(rule) { if (!(sheet && rule)) return false; var result = false; try { sheet.insertRule(rule, 0); result = !(/unknown/i).test(sheet.cssRules[0].cssText); sheet.deleteRule(sheet.cssRules.length - 1); } catch(e) { } return result; } : function(rule) { if (!(sheet && rule)) return false; sheet.cssText = rule; return sheet.cssText.length !== 0 && !(/unknown/i).test(sheet.cssText) && sheet.cssText .replace(/\r+|\n+/g, '') .indexOf(rule.split(' ')[0]) === 0; }; return supportAtRule('@font-face { font-family: "font"; src: "font.ttf"; }'); }; /****************************************************************************/ init(); //startup stuff } return FoutBGone; }));