Webkit - dynamically created stylesheet - when does it really load?
dom, javascript, jquery, webkit
Solution
Dynamically loading CSS stylesheets is still an area filled with browser quirks, unfortunately. In Webkit, `<style>` and `<link>` elements will both fire `load` and `error` events when loading stylesheets. However, the `load` event itself means only that the stylesheet resource has been loaded, not necessarily that it has been added to `document.styleSheets`.
The require-css RequireJS loader deals with this issue by branching its loading mechanism based on userAgent sniffing (it is nearly impossible to feature-detect whether or not the `<link>` tag will fire its `load` event properly). Specifically for Webkit, the detection resorts to using `setTimeout` to find when the StyleSheet object has been attached to `document.styleSheets`
var webkitLoadCheck = function(link, callback) {
setTimeout(function() {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (sheet.href == link.href)
return callback();
}
webkitLoadCheck(link, callback);
}, 10);
}
So while the `load` event will fire on Webkit, it is unreliable for the purposes of being able to access the corresponding `StyleSheet` instance. Currently the only engine that supports stylesheet `load` events properly is Firefox 18+.
Disclosure: I am a contributor to require-css
References:
- http://pieisgood.org/test/script-link-events/
- https://bugs.webkit.org/show_bug.cgi?id=56436
- https://bugs.webkit.org/show_bug.cgi?id=38995
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link
Problem
I have some code (it's actually not mine but the SlickGrid library) that creates a `<style>` element, inserts it into the DOM, then immediately tries to find the new stylesheet in the document.styleSheets collection. In WebKit this sometimes fails. I don't actually have any idea what the circumstances are, but it's nothing that's consistently reproducible. I figured I could work around it by changing the code so the check for the StyleSheet object doesn't happen until the `load` event on the style element, like so: ``` $style = $("<style type='text/css' rel='stylesheet' />").appendTo($("head")); var rules = ...;// code to create the text of the rules here if ($style[0].styleSheet) { // IE $style[0].styleSheet.cssText = rules.join(" "); } else { $style[0].appendChild(document.createTextNode(rules.join(" "))); } $style.bind('load', function() { functionThatExpectsTheStylesheet(); }); ``` and functionThatExpectsTheStylesheet attempts to locate the actual stylesheet object like so: ``` var sheets = document.styleSheets; for (var i = 0; i < sheets.length; i++) { if ((sheets[i].ownerNode || sheets[i].owningElement) == $style[0]) { stylesheet = sheets[i]; break; } } ``` but sometimes even at that point, the stylesheet object is not found. So, my question is this: - Does the `load` event in fact not guarantee that the styleSheet object will be available? Is it a bug? - If not, is there another condition that I can use or do I just need to do this using timeouts? - Or is there some problem with the way I'm binding the load event and that's my problem?