Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active July 28, 2020 23:28
Show Gist options
  • 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));
@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