Permalink
Browse files

added a maximum time that eCSSential will allow page rendering to be …

…blocked while a stylesheet loads. Default timeout is 8 seconds, at which point the page will be shown and the stylesheet will be refetched in a non-blocking manner. This option is configurable via the "patience" option.
  • Loading branch information...
1 parent e036cbe commit 8b7bbbcfe547c16b904dfe322f19eaf4b40b599b Scott Jehl committed Jun 8, 2012
Showing with 86 additions and 13 deletions.
  1. +38 −5 README.md
  2. +25 −4 dist/eCSSential.js
  3. +2 −2 dist/eCSSential.min.js
  4. +1 −0 examples/logdata.js
  5. +20 −2 src/eCSSential.js
View
@@ -8,12 +8,17 @@
Loading CSS in an optimized or prioritized fashion is very difficult. In order for a website to load cleanly, all CSS needed for rendering that page layout must be referenced in the `head` of a document. This is because stylesheets loaded in this way will block page rendering until they are loaded and ready to apply. If a stylesheet is referenced later in a document, or loaded dynamically via JS, users will often see a <abbr title="flash of unstyled content">FOUC</abbr> while that stylesheet loads concurrently with page rendering.
-Unfortunately, this limitation can make for a lot of overhead in responsive designs, particularly if a stylesheet contains a large amount of CSS for breakpoints that don't currently apply at a particular viewport size, or worse, CSS that won't ever apply on a particular device. More unfortunate, using separate `link` elements with `media` attributes to reference stylesheets with their intended breakpoints [doesn't prevent those stylesheets from downloading and blocking page rendering](http://scottjehl.github.com/CSS-Download-Tests/), even in environments where they don't currently or will never apply.
+Unfortunately, this limitation can make for a lot of overhead in responsive designs, particularly if a stylesheet contains a large amount of CSS for breakpoints that don't currently apply at a particular viewport size, or worse, CSS that won't ever apply on a particular device. More unfortunate, using separate `link` elements with `media` attributes to reference stylesheets with their intended breakpoints [doesn't prevent those stylesheets from downloading and blocking page rendering](http://scottjehl.github.com/CSS-Download-Tests/), even in environments where they don't currently or will never apply.
+
+Lastly, if for some reason a stylesheet takes a long time to load, most browsers will let it continue to block page rendering for 30 seconds or more!
### How eCSSential Helps
-eCSSential is a JavaScript utility that is designed to make browsers download files in a faster, more responsible manner than they do by default. Technically speaking, it is a teeny bit of inline JavaScript that determines which of your stylesheets should be loaded immediately and block page rendering (any stylesheets intended for mobile-first breakpoints that currently apply), which stylesheets should be deferred to load asynchronously (any stylesheets intended for breakpoints that don't currently apply to the current viewport size, but could apply later, given the device's screen size), and which stylesheets should never be loaded at all (any stylesheets intended for viewport dimensions that are larger than the device's screen). Once sorted, the essential (or eCSSential if you will) files are loaded in a way that ensures page rendering will be blocked until they're ready. The other less-essential files are loaded in a non-blocking way, letting the page render while they are fetched.
+eCSSential is a JavaScript utility that is designed to make browsers download files in a faster, more responsible manner than they do by default. Technically speaking, it is a tiny bit of JavaScript that when placed in the `head` of a page, determines which of your stylesheets should be loaded immediately and block page rendering (any stylesheets intended for mobile-first breakpoints that currently apply), which stylesheets should be deferred to load asynchronously (any stylesheets intended for breakpoints that don't currently apply to the current viewport size, but could apply later, given the device's screen size), and which stylesheets should never be loaded at all (any stylesheets intended for viewport dimensions that are larger than the device's screen). Once sorted, the essential (or eCSSential if you will) files are loaded in a way that ensures page rendering will be blocked until they're ready. The other less-essential files are loaded in a non-blocking way, letting the page render while they are fetched.
+
+In one further improvement to browsers' default loading behavior, stylesheets that are loaded in a blocking manner are given 8 seconds (by default) to load before they are refetched asynchonously, allowing the page to appear and be used.
+
## Check out the demos
@@ -70,15 +75,28 @@ Because eCSSential requires JavaScript support to perform its optimizations, you
That's it! With eCSSential in place, your pages will now render much faster on many devices (particularly small screens).
+## Setting configuration options
+
+eCSSential comes with a number of defaults that can be overridden on a per-call basis. These options are configured by passing a second argument to the `eCSSential` function in the form of an object with one or more key/value pairs.
+
+ eCSSential( {
+ "all": "css/all.css",
+ "(min-width: 20em)": "css/min-20em.css",
+ [_...more files..._]
+ },
+ // SET CONFIGURATION OPTIONS HERE
+ { optionA: true, optionB: 500 } );
+
+The following sections will reference configuration options that are defined in this manner.
+
+
## Optimizing Further with File Concatenation
By default, eCSSential creates individual `link` elements for each stylesheet it requests, which, depending on the number of CSS files you have, can make for a lot of HTTP requests. Reducing HTTP requests is one of the best ways to improve the performance of a site, so eCSSential is designed to work with concatenated files if you instruct it to do so.
Optionally, eCSSential can be configured to fetch all of your CSS via only 2 HTTP requests, of which the first request is immediate and blocking (synchronous), and the second is deferred and non-blocking (asynchronous). To use this feature, you'll need the help of a server-side concatenation tool, such as [QuickConcat](https://github.com/filamentgroup/quickconcat), or a build system that generates all static versions of your potential CSS file combinations. The `examples` directory contains a demo of this feature. You can also find it [here](http://scottjehl.github.com/eCSSential/examples/concat). The demo uses static generated combinations of the CSS files.
-To configure eCSSential to fetch concatenated files via a single request, you'll need to define a second argument when you call `eCSSential()`.
-
-That argument should be a JavaScript object, and it should include a `concat` method. The only rules for that method is that it should accept an array and return a string. For example, if your concatenated CSS files have filenames that are a long joined name of the files they contain, separated by periods with their directories and extensions removed, you might define a `concat` option like this:
+To configure eCSSential to fetch concatenated files via a single request, you'll need to define a configuration property of `concat` as a function. The only rules for that `concat` function is that it should accept an array and return a string. For example, if your concatenated CSS files have filenames that are a long joined name of the files they contain, separated by periods with their directories and extensions removed, you might define a `concat` option like this:
eCSSential({
"all": "css/all.css",
@@ -147,6 +165,21 @@ Alternatively, if you would like to load all of your stylesheets in IE 6-8 polyf
When doing this, just be sure to add [Respond.js](https://github.com/scottjehl/Respond) or an equivalent workaround after the references to these CSS files.
+
+## Changing the maximum time rendering will block
+
+By default, eCSSential will allow a blocking stylesheet to load for 8 seconds before refetching it and showing the page in whatever state it may be. If you'd like to change this timeout, just pass a different millisecond-based `patience` value in the configuration object, like so:
+
+ eCSSential({
+ "all": "all.css",
+ "(min-width: 20em)": "css/min-20em.css",
+ "(min-width: 37.5em)": "css/min-37.5em.css",
+ "(min-width: 50em)": "css/min-50em.css",
+ "(min-width: 62.5em)": "css/min-62.5em.css"
+ //set the max rendering timeout to 6 seconds instead of 8
+ }, { patience: 6000 } );
+
+
## Disabling the default deferred stylesheet qualifier
By default, eCSSential will not load stylesheets that are targeted at dimensions not possible on a particular device (based on its screen size). This ammounts to better performance on small screens by reducing HTTP requests, but it does have the potential drawback that if the browser window is moved to a different sized screen, it may not have all of the styles it needs optimize for that new screen. This is somewhat of an edge case, but if you'd like to make sure every non-applicable CSS file is loaded asynchonously, you can pass the `deferAll` option as true.
View
@@ -1,4 +1,4 @@
-/*! eCSSential - v0.1.0 - 2012-06-07
+/*! eCSSential - v0.1.0 - 2012-06-08
* https://github.com/scottjehl/eCSSential
* Copyright (c) 2012 Scott Jehl, @scottjehl, Filament Group, Inc.; Licensed GPL, MIT; Includes matchMedia.js: http://j.mp/jay3wJ (MIT) */
@@ -30,11 +30,15 @@ window.matchMedia = window.matchMedia || (function(doc, undefined){
};
}(document));
-/*! eCSSential.js: An experiment in optimized loading of mobile-first responsive CSS. [c]2012 @scottjehl, MIT/GPLv2 */
+/*! eCSSential
+* https://github.com/scottjehl/eCSSential
+* Copyright (c) 2012 Scott Jehl, @scottjehl, Filament Group, Inc.; Licensed GPL, MIT; Includes matchMedia.js: http://j.mp/jay3wJ (MIT) */
+
window.eCSSential = function( css, config ){
"use strict";
var load = [],
defer = [],
+ timedout = [],
// All options false or null by default; no need for a mixin
o = config || {},
w = window,
@@ -95,10 +99,27 @@ window.eCSSential = function( css, config ){
}
}
- // document.write the stylesheet that should block
+ // document.write the stylesheets that should block
if( load.length ){
d.write( makeLinks( load ) );
insLoc = d.getElementById( "eCSS" );
+
+ // set up timeout to stop a stylesheet from blocking after 8 seconds
+ // or by however many ms are passed via o.patience
+ var links = insLoc.parentNode.getElementsByTagName( "link" );
+ for(var i = 0, il = links.length; i< il; i++ ){
+ (function( c ){
+ var t = w.setTimeout(function(){
+ var next = c.nextSibling;
+ c.parentNode.removeChild( c );
+ next.parentNode.insertBefore( c, next );
+ timedout.push( c );
+ }, o.patience || 8000 );
+ c.onload = function(){
+ clearTimeout( t );
+ };
+ }( links[ i ] ));
+ }
}
// defer the load of the stylesheet that could later apply
@@ -108,5 +129,5 @@ window.eCSSential = function( css, config ){
insLoc.parentNode.insertBefore( div, insLoc );
}
// return data for testing
- return { css: css, config: config, block: load, defer: defer };
+ return { css: css, config: config, block: load, defer: defer, timedout: timedout };
};
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
@@ -78,6 +78,7 @@
info.push("<dt>Total number of CSS requests</dt><dd>"+ numlinks +"</dd>");
info.push("<dt>Total number of blocking CSS requests</dt><dd>"+ (loadCSS.config && loadCSS.config.concat && loadCSS.block.length ? 1 : loadCSS.block.length ) +"</dd>");
info.push("<dt>Total number of non-blocking CSS requests</dt><dd>"+ (loadCSS.config && loadCSS.config.concat && loadCSS.defer.length ? 1 : loadCSS.defer.length) +"</dd>");
+ info.push("<dt>CSS files that timed out and were refetched</dt><dd>"+ (loadCSS.length ? loadCSS.timedout.join(", ") : "0" ) +"</dd>");
info.push("<dt>Initial viewport dimensions</dt><dd>Width: "+ window.innerWidth +", Height: "+ window.innerHeight +"</dd>");
info.push("<dt>Initial screen dimensions</dt><dd>Width: "+ screen.width +", Height: "+ screen.height +"</dd>");
info.push("</dl>");
View
@@ -6,6 +6,7 @@ window.eCSSential = function( css, config ){
"use strict";
var load = [],
defer = [],
+ timedout = [],
// All options false or null by default; no need for a mixin
o = config || {},
w = window,
@@ -66,10 +67,27 @@ window.eCSSential = function( css, config ){
}
}
- // document.write the stylesheet that should block
+ // document.write the stylesheets that should block
if( load.length ){
d.write( makeLinks( load ) );
insLoc = d.getElementById( "eCSS" );
+
+ // set up timeout to stop a stylesheet from blocking after 8 seconds
+ // or by however many ms are passed via o.patience
+ var links = insLoc.parentNode.getElementsByTagName( "link" );
+ for(var i = 0, il = links.length; i< il; i++ ){
+ (function( c ){
+ var t = w.setTimeout(function(){
+ var next = c.nextSibling;
+ c.parentNode.removeChild( c );
+ next.parentNode.insertBefore( c, next );
+ timedout.push( c );
+ }, o.patience || 8000 );
+ c.onload = function(){
+ clearTimeout( t );
+ };
+ }( links[ i ] ));
+ }
}
// defer the load of the stylesheet that could later apply
@@ -79,5 +97,5 @@ window.eCSSential = function( css, config ){
insLoc.parentNode.insertBefore( div, insLoc );
}
// return data for testing
- return { css: css, config: config, block: load, defer: defer };
+ return { css: css, config: config, block: load, defer: defer, timedout: timedout };
};

0 comments on commit 8b7bbbc

Please sign in to comment.