Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active July 28, 2020 23:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfkaye/4773010 to your computer and use it in GitHub Desktop.
Save dfkaye/4773010 to your computer and use it in GitHub Desktop.
requestCss.js - stylesheet loader api for javaScript - attempts to work around lack-of-support for css load events && the rule number limits in MSIE
/**
* file: css-api.js - provides stylesheet requestCss api to JavaScript -
* author: @dfkaye - david.kaye
* date: 2013-2-12
* fixed whitespace: 2020-07-28
*
* Prior Art:
* - http://www.phpied.com/when-is-a-stylesheet-really-loaded/
* - http://www.zachleat.com/web/load-css-dynamically/
*
* To-DO
* - commonjs module support for global scope and exports
* - better (any) negative tests for bad URLs
*
*/
(function (exports) {
/* public */
exports.requestCss = requestCss;
/* local */
var requested = {};
/*
* params - any number of string arguments to css filepaths.
* The optional callback function should be the last argument.
*/
function requestCss() {
/*
* <link> elements don't fire load event cross-browser
* (webkit, FF < 9 - opera uses addEventListener...),
* plus IE restricts <link> and @import counts to 31, nesting 3 deep, etc.
* BUT, <style> elements fire load events!
* AND, we can use multiple imports in a <style> to beat the IE restriction (no kidding!).
*/
var style = document.createElement('style');
style.setAttribute('media', 'all');
var args = arguments;
var len = args.length;
var callback = args[len - 1];
if (typeof callback === 'function') {
len = len - 1;
style.onload = function () {
style.onload = null;
callback();
};
}
var pending = len;
var cssText = '';
// Append the element right away so that the @import directive runs on an
// active element; borks out otherwise.
document.getElementsByTagName('head')[0].appendChild(style);
for (var i = 0; i < len; i++) {
var url = args[i];
if (typeof url == 'string' && !(url in requested)) {
requested[url] = url;
try {
cssText += "\n@import url(" + url + ");";
} catch (err) {
console.error(err + ': ' + url);
} finally {
continue;
}
}
}
// try not to block other processes
setTimeout(function () {
if (style.styleSheet) {
// internet explorer
style.styleSheet.cssText = cssText;
} else {
// most DOM-compliant browsers
style.appendChild(document.createTextNode(cssText));
}
}, 0);
};
}(this));
@3rd-Eden
Copy link

I've took the code for a spin, it seems that the onload callback is indeed executed in various of browsers. But the problem with this is that the event is fired before the stylesheets are actually loaded.

I've added a small reproducable testcase here in JSBin: http://jsbin.com/azudux/3/

In addition to that, the library doesn't fully resolve the issues in IE. If you do 32 requests with one stylesheet you would still hit the same limit as it's not re-using previous stylesheets. I did find this great resource for figuring out the loading limits of IE:

http://john.albin.net/ie-css-limits/single-style-test.html

@dfkaye
Copy link
Author

dfkaye commented Jul 22, 2013

STATUS as of 22 JUL 2013

I've added a test repo here => https://github.com/dfkaye/css-request-test/ - taking the cuzillion test cases you put up on jsbin. You can view the rawgithub page in action here => http://rawgithub.com/dfkaye/css-request-test/master/index.html

So far looks like firefox does it right. I'm checking other rules or attributes in chrome which seems to load 'done' eagerly. More to come.

That cuzillion delay is definitely a pain - it ends up aborting in MSIE 10 and various compat modes for me - I'll see if I can figure out how to assign a real "done" property that the callback can check...

There's already something wrong when we're making 32 requests to load into a single stylesheet. The point of this lib was to load an arbitrary number of style tags with a manageable number of imports (like 12).

MORE TO COME (LATER TONIGHT)....

@dfkaye
Copy link
Author

dfkaye commented Jul 23, 2013

"Uncle." - loading multiple delayed css in imports in a single style tag has uncannily blocking behavior in IE, callsback eagerly in Chrome but before the style is actually applied, blows up in Firefox unless you use try/catch for the style.sheet.cssRules read which is not accessible for cross-domain css, and never fires the final callback in Opera.

From my perspective, however, IE has this correct and all the others are off the mark.

To give IE a break from blocking imports, I've redone the repo example with link tags instead of style tags plus imports, and use the img prefetch trick by stoyan stefanov. All 4 browsers seem to fire both callbacks after the second stylesheet arrives and the rules are applied.

repo: https://github.com/dfkaye/css-request-test/

test page: http://rawgithub.com/dfkaye/css-request-test/master/index.html

using js file at https://github.com/dfkaye/css-request-test/blob/master/js/img-css-request.js

@dfkaye
Copy link
Author

dfkaye commented Jul 23, 2013

Updated - fallback file for IE9-- uses style/import strategy, the rest use link strategy - see test page for latest

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