Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The deferred font loading logic for Smashing Magazine. http://www.smashingmagazine.com/
<script type="text/javascript">
(function () {
"use strict";
// once cached, the css file is stored on the client forever unless
// the URL below is changed. Any change will invalidate the cache
var css_href = './index_files/web-fonts.css';
// a simple event handler wrapper
function on(el, ev, callback) {
if (el.addEventListener) {
el.addEventListener(ev, callback, false);
} else if (el.attachEvent) {
el.attachEvent("on" + ev, callback);
}
}
// if we have the fonts in localStorage or if we've cached them using the native batrowser cache
if ((window.localStorage && localStorage.font_css_cache) || document.cookie.indexOf('font_css_cache') > -1){
// just use the cached version
injectFontsStylesheet();
} else {
// otherwise, don't block the loading of the page; wait until it's done.
on(window, "load", injectFontsStylesheet);
}
// quick way to determine whether a css file has been cached locally
function fileIsCached(href) {
return window.localStorage && localStorage.font_css_cache && (localStorage.font_css_cache_file === href);
}
// time to get the actual css file
function injectFontsStylesheet() {
// if this is an older browser
if (!window.localStorage || !window.XMLHttpRequest) {
var stylesheet = document.createElement('link');
stylesheet.href = css_href;
stylesheet.rel = 'stylesheet';
stylesheet.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(stylesheet);
// just use the native browser cache
// this requires a good expires header on the server
document.cookie = "font_css_cache";
// if this isn't an old browser
} else {
// use the cached version if we already have it
if (fileIsCached(css_href)) {
injectRawStyle(localStorage.font_css_cache);
// otherwise, load it with ajax
} else {
var xhr = new XMLHttpRequest();
xhr.open("GET", css_href, true);
// cater for IE8 which does not support addEventListener or attachEvent on XMLHttpRequest
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// once we have the content, quickly inject the css rules
injectRawStyle(xhr.responseText);
// and cache the text content for further use
// notice that this overwrites anything that might have already been previously cached
localStorage.font_css_cache = xhr.responseText;
localStorage.font_css_cache_file = css_href;
}
};
xhr.send();
}
}
}
// this is the simple utitily that injects the cached or loaded css text
function injectRawStyle(text) {
var style = document.createElement('style');
// cater for IE8 which doesn't support style.innerHTML
style.setAttribute("type", "text/css");
if (style.styleSheet) {
style.styleSheet.cssText = text;
} else {
style.innerHTML = text;
}
document.getElementsByTagName('head')[0].appendChild(style);
}
}());
</script>
@JimmyRittenborg

This comment has been minimized.

Show comment
Hide comment
@JimmyRittenborg

JimmyRittenborg Jun 30, 2014

Seems like Basket would be a great fit for this too? :)

Seems like Basket would be a great fit for this too? :)

@hdragomir

This comment has been minimized.

Show comment
Hide comment
@hdragomir

hdragomir Jul 2, 2014

@JimmyRittenborg yes, you could. The script itself has no dependencies, but you can use it however you like :-)

Owner

hdragomir commented Jul 2, 2014

@JimmyRittenborg yes, you could. The script itself has no dependencies, but you can use it however you like :-)

@Nitij

This comment has been minimized.

Show comment
Hide comment
@Nitij

Nitij Jul 4, 2014

This code should have been made generic for any kind of style or script resource.

Nitij commented Jul 4, 2014

This code should have been made generic for any kind of style or script resource.

@hdragomir

This comment has been minimized.

Show comment
Hide comment
@hdragomir

hdragomir Jul 7, 2014

@Nitij have a look at this simple change: https://gist.github.com/hdragomir/9b5dd976b7477a9a8c23

The whole point of this is to have it executed as soon as possible, though. Please bare that in mind.

Owner

hdragomir commented Jul 7, 2014

@Nitij have a look at this simple change: https://gist.github.com/hdragomir/9b5dd976b7477a9a8c23

The whole point of this is to have it executed as soon as possible, though. Please bare that in mind.

@Robdel12

This comment has been minimized.

Show comment
Hide comment
@Robdel12

Robdel12 Jul 16, 2014

I noticed a flash if you refresh in chrome 35.0.

Here's a video example.
https://www.youtube.com/watch?v=zsUETQ--j0s

Have any ideas?

I noticed a flash if you refresh in chrome 35.0.

Here's a video example.
https://www.youtube.com/watch?v=zsUETQ--j0s

Have any ideas?

@enapupe

This comment has been minimized.

Show comment
Hide comment
@enapupe

enapupe Sep 1, 2014

The window.onload needs a setTimeout fallback in case any resource hangs on loading forever..

enapupe commented Sep 1, 2014

The window.onload needs a setTimeout fallback in case any resource hangs on loading forever..

@adamhavel

This comment has been minimized.

Show comment
Hide comment
@adamhavel

adamhavel Sep 11, 2014

Great script. Unfortunately, this does not work in IE8 due to the use of style.innerHTML. You'd have to employ style.styleSheet.cssText instead. I made a streamlined version here:

https://gist.github.com/adamhavel/d7c87f6dcde81a6fbd8f

One thing to note: I don't care about anything less that IE8, that's why I drop the localStorage check.

Great script. Unfortunately, this does not work in IE8 due to the use of style.innerHTML. You'd have to employ style.styleSheet.cssText instead. I made a streamlined version here:

https://gist.github.com/adamhavel/d7c87f6dcde81a6fbd8f

One thing to note: I don't care about anything less that IE8, that's why I drop the localStorage check.

@adamhavel

This comment has been minimized.

Show comment
Hide comment
@adamhavel

adamhavel Sep 16, 2014

I modified my gist so that it goes through an array of files and loads them all, according to their type. As of the moment, I added support for inlining svg (to be used for example with svg symbol technique). Also, less localStorage reads = win.

I modified my gist so that it goes through an array of files and loads them all, according to their type. As of the moment, I added support for inlining svg (to be used for example with svg symbol technique). Also, less localStorage reads = win.

@adamhavel

This comment has been minimized.

Show comment
Hide comment
@adamhavel

adamhavel Sep 20, 2014

Version without support for IE8 (or to be used with polyfills):
https://gist.github.com/adamhavel/57992ed5b222e47c8856

Version without support for IE8 (or to be used with polyfills):
https://gist.github.com/adamhavel/57992ed5b222e47c8856

@frontendbeast

This comment has been minimized.

Show comment
Hide comment
@frontendbeast

frontendbeast Oct 21, 2014

IE8 doesn't support:

  • style.innerHTML
  • addEventListener or attachEvent on XMLHttpRequest

So I've forked, updated and tested the above to work on IE8.

https://gist.github.com/iamdarrenhall/495771696b6e270508be

IE8 doesn't support:

  • style.innerHTML
  • addEventListener or attachEvent on XMLHttpRequest

So I've forked, updated and tested the above to work on IE8.

https://gist.github.com/iamdarrenhall/495771696b6e270508be

@hdragomir

This comment has been minimized.

Show comment
Hide comment
@hdragomir

hdragomir Oct 21, 2014

@iamdarrenhall I have updated this one as well. I had not considered IE8 at all, thank you for the time! :-)

Owner

hdragomir commented Oct 21, 2014

@iamdarrenhall I have updated this one as well. I had not considered IE8 at all, thank you for the time! :-)

@frontendbeast

This comment has been minimized.

Show comment
Hide comment
@frontendbeast

frontendbeast Oct 22, 2014

@hdragomir Thank you! I needed it for a project I'm working on. You committed a spelling mistake by the way, on line 16 – "native batrowser cache" ;-)

@hdragomir Thank you! I needed it for a project I'm working on. You committed a spelling mistake by the way, on line 16 – "native batrowser cache" ;-)

@christianseel

This comment has been minimized.

Show comment
Hide comment
@christianseel

christianseel Oct 29, 2014

I had an issue with IE9+ that it didn't show my fonts (the css was loaded successfully) on the first request (with an empty localstorage). Looks like IE doesn't render the lately injected style tag.
Is anybody experiencing the same?
I "fixed" it by adding an empty style tag to my html and filling this tag in the injectRawStyle function instead of adding a new tag.

Update: I found a better fix at http://www.phpied.com/dynamic-script-and-style-elements-in-ie/
It's important for IE to append the style tag before setting it's content! And it works!

I had an issue with IE9+ that it didn't show my fonts (the css was loaded successfully) on the first request (with an empty localstorage). Looks like IE doesn't render the lately injected style tag.
Is anybody experiencing the same?
I "fixed" it by adding an empty style tag to my html and filling this tag in the injectRawStyle function instead of adding a new tag.

Update: I found a better fix at http://www.phpied.com/dynamic-script-and-style-elements-in-ie/
It's important for IE to append the style tag before setting it's content! And it works!

@tobek

This comment has been minimized.

Show comment
Hide comment
@tobek

tobek Dec 8, 2014

@hdragomir if the problem is unreliable browser caching of fonts, why not just base64-encode your fonts in CSS and load that as normal (or asynchronously), skipping localStorage altogether? Or should we consider all browser caching to be unreliable, and use localStorage for all long-term static assets?

tobek commented Dec 8, 2014

@hdragomir if the problem is unreliable browser caching of fonts, why not just base64-encode your fonts in CSS and load that as normal (or asynchronously), skipping localStorage altogether? Or should we consider all browser caching to be unreliable, and use localStorage for all long-term static assets?

@frontendbeast

This comment has been minimized.

Show comment
Hide comment
@frontendbeast

frontendbeast Jan 16, 2015

I've added a check to see if the XMLHTTPRequest response status code is 200, as it was caching a 404 page when the CSS file didn't get deployed.

Forked, updated and tested:
https://gist.github.com/iamdarrenhall/53b5eea54ac4bc25d0a6

Or just change line 54 to:

if (xhr.readyState === 4 && xhr.status === 200) {

I've added a check to see if the XMLHTTPRequest response status code is 200, as it was caching a 404 page when the CSS file didn't get deployed.

Forked, updated and tested:
https://gist.github.com/iamdarrenhall/53b5eea54ac4bc25d0a6

Or just change line 54 to:

if (xhr.readyState === 4 && xhr.status === 200) {
@frontendbeast

This comment has been minimized.

Show comment
Hide comment
@frontendbeast

frontendbeast Feb 3, 2015

I've done another update to prevent IE8 crashing. Moved the document.getElementsByTagName('head')[0].appendChild(style); above the style.styleSheet.cssText = text;

https://gist.github.com/iamdarrenhall/53b5eea54ac4bc25d0a6

I've done another update to prevent IE8 crashing. Moved the document.getElementsByTagName('head')[0].appendChild(style); above the style.styleSheet.cssText = text;

https://gist.github.com/iamdarrenhall/53b5eea54ac4bc25d0a6

@ginsterbusch

This comment has been minimized.

Show comment
Hide comment
@ginsterbusch

ginsterbusch Oct 22, 2015

@iamdarrenhall + @adamhavel: which is pretty weird, when you think about it. after all, it was IE who introduced us to innerHTML.

cu, w0lf.

@iamdarrenhall + @adamhavel: which is pretty weird, when you think about it. after all, it was IE who introduced us to innerHTML.

cu, w0lf.

@tigranpetrossian

This comment has been minimized.

Show comment
Hide comment
@tigranpetrossian

tigranpetrossian Nov 9, 2015

Is there a way to do the same with just font files without combining them into a base encoded CSS file? Some web-font providers unfortunately don't allow to do that even if you're self hosting.

Is there a way to do the same with just font files without combining them into a base encoded CSS file? Some web-font providers unfortunately don't allow to do that even if you're self hosting.

@kurtextrem

This comment has been minimized.

Show comment
Hide comment
@kurtextrem

kurtextrem Jan 24, 2017

Take a look at my version, which is much more versatile: https://github.com/kurtextrem/localFont/blob/gh-pages/launcher.html
(I'd recommend to use a conditional for IE8 instead of loading it through the script though)

Take a look at my version, which is much more versatile: https://github.com/kurtextrem/localFont/blob/gh-pages/launcher.html
(I'd recommend to use a conditional for IE8 instead of loading it through the script though)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment