An asynchronous CSS loading promise library
A brilliant library for asynchronously loading CSS and executing callbacks via a cross-browser compatible promise API.
Inspired by: loadCSS, css-onload.js, and Only.js.
There are basically four reasons why CSS.load was born. They are (in no particular order):
link[rel=stylesheet]
or @import
are being downloaded.
CSS.load ensures you're able to load non-critical stylesheets asynchronously, so they don't block page rendering.
In it's basic form, simply call the load
function and pass it a stylesheet URL:
$CSS().load("path/to/stylesheet.css");
The code above will insert a new CSS stylesheet link
after the first meta
CSS.load finds in the head, and will return a chainable promise-like callback API with a reference to the link
.
Multiple calls to CSS.load will reference CSS files in reverse the order they are called, but may finish loading in a different order
than they were called depending on network conditions.
Newer browsers are beginning to support a standard markup pattern for loading CSS (and other file types) asynchronously:
<link rel="preload">
(W3C Spec).
The markup for referencing your CSS file looks like this:
<link rel="preload" href="path/to/stylesheet.css" as="style">
<noscript><link rel="stylesheet" href="path/to/stylesheet.css"></noscript>
The current implementation of this markup pattern is to load these asynchronous CSS with low priority. CSS.load will act as a polyfill for browsers that support or don't yet support this new feature by automatically converting them to stylesheets (and download with higher priority).
NOTE: rel=preload
does not apply the CSS on its own. CSS.load will try fetching these stylesheets asynchronously
(the browser cache will be used for supported browsers) and apply the CSS as soon as the CSS
finishes loading. Since CSS.load requires JavaScript, you may want to include a reference to your CSS file using a
noscript
element to ensure it only applies in non-JavaScript environments.
NOTE: Be sure that your CSS file will be referenced from the same spot in the source order as the original link
element whether the browser supports rel=preload
or not. Keep this in mind, as you may want to place the
link
in a particular location in your head
element so that the CSS loads with an expected cascade order.
You can view a demo of this rel=preload
pattern below: # Online Tests
When you're calling CSS.load manually (without the rel=preload
pattern.
[$CSS] $CSS(object optionsObject)
Sets one or more options to be in effect for the current chain being executed. This method is chainable and must be chained.
It is (obviously) the first call in the chain (as it internally calls a setOptions()
method).
Parameters
optionsObject : an object which contains name/value pairs for the options to set:
document.querySelector()
.
If the supplied selector doesn't exist (ie, null) it will finally default to the first 'meta' element in the <head>
.
<link>
should be inserted before
or after selector defined by the insertAtElement option.
Returns
$CSS : the chained object reference so subsequent calls to load() can be made.
$CSS({
insertAtElement: 'link.theme-base', // the first link tag with class="theme-base"
insertBefore: true, // insert before 'link.theme-base' element
basePath: '//cdn.my-cdn.com/assets/css/', // use a different base path
allowDuplicates: true, // browser may fetch CSS from cache
cacheBust: true // always request a fresh CSS
});
[$CSS] $CSS().load(varies,...)
This method accepts one or more parameters of varying types. Each parameter value is intended to specify a stylesheet resource URI to load.
Parameters
varies (can be any number/combination of the following):
<link>
element:
function : if a function is found as one of the parameters, that function will be be executed immediately, which must return a value directly. The return value must be one of the other allowable types (string, object, or array). If the function call results in no return value ("undefined") or the value is "falsy" (false, null, etc), it will be interpreted as no stylesheet found.
This feature allows the construction of "conditional chains", where run-time logic is used to decide ultimately what stylesheet will be loaded by the $CSS chain.
Returns
$CSS : the chained promise object reference so subsequent calls to done(),
fail() and always() can be made.
$CSS().load([
"path/to/stylesheet-1.css",
["path/to/stylesheet-2.css", "path/to/stylesheet-3.css"],
{
href: "path/to/stylesheet-4.css",
class: "print-theme",
media: "print"
},
function(){
// can perform any funny logic here :)
return "path/to/stylesheet-5.css";
}
], "path/to/stylesheet-6.css", "path/to/stylesheet-7.css");
NOTE: The returned chained object does not include load() ie, subsequent calls to load() such as
$CSS().load().load()
cannot be made. This feature is intentional.
[$CSS] $CSS().load().done(function inlineScript(ss_obj)[=null])
This function is used to run a callback function reference (usually an inline anonymous function) when each stylesheet
specified with .load()
has been successfully loaded.
Parameters
inlineScript : The callback function to run when stylesheet has been successfully loaded. Sets a reference
to the current stylesheet that was successfully loaded as the first parameter.
Returns
$CSS : the chained promise object reference so subsequent calls to done(),
fail() and always() can be made.
$CSS().load(["path/to/stylesheet-1.css", "path/to/stylesheet-2.css"])
.done(function(ss_obj){
// attribute 'data-href' holds the default uri before any uri rewrite
window.console.info(ss_obj.getAttribute('data-href') + ' was successfully loaded');
});
[$CSS] $CSS().load().fail(function inlineScript(ss_obj)[=null])
This function is used to run a callback function reference (usually an inline anonymous function) when each stylesheet
specified with .load()
has failed to load.
Parameters
inlineScript : The callback function to run when stylesheet has failed to load. Sets a reference
to the current stylesheet that failed to load as the first parameter.
Returns
$CSS : the chained promise object reference so subsequent calls to done(),
fail() and always() can be made.
$CSS().load(["path/to/stylesheet-1.css", "path/to/stylesheet-2.css"])
.fail(function(ss_obj){
// attribute 'data-href' holds the default uri before any uri rewrite
window.console.info(ss_obj.getAttribute('data-href') + ' failed to load');
});
[$CSS] $CSS().load().always(function inlineScript(ss_obj)[=null])
This function is used to run a callback function reference (usually an inline anonymous function) when each stylesheet
specified with .load()
has either successfully loaded or failed to load.
Parameters
inlineScript : The callback function to run when stylesheet has either successfully loaded or failed to load. Sets a reference
to the current stylesheet that has either successfully loaded or failed to load as the first parameter.
Returns
$CSS : the chained promise object reference so subsequent calls to done(),
fail() and always() can be made.
$CSS().load(["path/to/stylesheet-1.css", "path/to/stylesheet-2.css"])
.always(function(ss_obj){
// attribute 'data-href' holds the default uri before any uri rewrite
window.console.info(ss_obj.getAttribute('data-href') + ' was either successful or unsuccessful');
});
[$CSS] $CSS().noConflict(none)
noConflict() rolls back the page to the previous version of $CSS (if any) before CSS.load was executed, and returns the current instance of $CSS.
Parameters
none
Returns
$CSS() instance : the current $CSS() instance (from before the rollback)
window.loadCSS = $CSS().noConflict();
loadCSS().load("path/to/stylesheet.css");
I typically use CSS.load
to load CSS files that are non-critical to the initial rendering of a page.
NOTE: The rel=preload
pattern is used for faster asynchronous stylesheet loading as it is fetched as
soon as CSS.load is executed. See this page's source for a live example of how it's used to improve page loading performance.
Below are in-browser tests:
CSS.load attempts to load a css file asynchronously in any JavaScript-capable browser. However, some older browsers will block rendering while the stylesheet is loading. This table outlines css loading support and async loading support.
Browser | CSS Loads Successfully | CSS Loads without Blocking Render |
---|---|---|
Chrome Mac (latest and many recent versions) | Yes | Yes |
Firefox Desktop (latest and many recent versions) | Yes | Yes |
Opera Mac (latest and many recent versions) | Yes | Yes |
Safari Mac (latest and many recent versions) | Yes | Yes |
Safari iOS (latest and many recent versions) | Yes | Yes |
Chrome Android 5.x | Yes | Yes |
Chrome Android 4.x | Yes | Yes |
Android Browser 2.3 | Yes | No |
Kindle Fire HD | Yes | Yes |
Windows Phone IE 8.1 | Yes | Yes |
IE 11 | Yes | Yes |
IE 10 | Yes | Yes |
IE 9 | Yes | Yes |
IE 8 | Yes | Yes |
IE 7 (no querySelector() support) | Yes | Yes |
IE 6 (no querySelector() support) | Yes | No |