Webkit - dynamically created stylesheet - when does it really load?

I have a code (it’s actually not mine, but SlickGrid ) that creates the <style> element, inserts it into the DOM, and then immediately tries to find the new stylesheet in the document.styleSheets collection.

In WebKit, this sometimes fails. I have no idea what the circumstances are, but this is nothing that is consistently reproducible. I decided that I could get around this by changing the code, so the StyleSheet object is not checked before the load event in the style element, for example:

  $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 tries to find the actual style sheet object like this:

  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 this point the style sheet object is not found.

So my question is this:

  • Does the load event not guarantee that the styleSheet object will be available? This is mistake?
  • If not, is there another condition that I can use, or do I just need to do it using timeouts?
  • Or are there some problems with how I bind the load event and what is my problem?
+8
javascript jquery dom webkit
source share
4 answers

Dynamically loading CSS style sheets is still an area filled with browser addictions, unfortunately. In Webkit, the <style> and <link> elements will load fire load and error events when loading stylesheets. However, the load event itself only means that a stylesheet resource has been loaded, it is not necessary that it be added to document.styleSheets .

The require-css boot loader RequireJS addresses this issue by forking its sniffing userAgent-based boot mechanism (it is almost impossible to detect a function whether the <link> tag fires or not, its load event). In particular, for Webkit, detection resorts to using setTimeout to find when the StyleSheet object was 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 fires in Webkit, it is unreliable to access the corresponding instance of StyleSheet . Currently, the only engine that supports load style events is Firefox 18+.

Disclosure: I am a require-css contributor

Literature:

+6
source share
  • I think the load event can be fired earlier than the css stylesheet. The problem may occur, possibly 1 out of 10 times, but its still not the best practice and the cleanest way.

  • setTimeout is definitely a good approach, as you can ensure that your script loads css first. However, since it is only a style sheet tag, it means that a link to it is created, css must first be loaded, so any approach of the two that you use never guarantees 100% that your css is loaded before the fire loading event.

To be 100% sure that everything works in the correct order, you can make a synchronous fileread via AJAX or an asynchronous AJAX call with a helper function that checks if CSS is ready (this way you don't freeze the browser, but still have the ability to check the download file or not). Another way would be to simply link the style guide: first set the stylesheets, and then enter the code in the header of your HTML file.

I was not so deeply immersed in load events, but it could happen that the DOMContentLoaded event is fired only when CSS is loaded (poor research on this)

Look at a similar question here: jQuery event that fires after loading CSS?

0
source share

I do not claim to have a solution, nor an explanation of Webkit load time, but I recently had a similar problem, but with JS: I had to be absolutely sure that the jQuery structure was loaded before I included other libraries that depended from jQuery.

Then I noticed that the load jQuery event was not fired when I expected it.

For what it's worth, here's how I solved my problem:

 var addedHead = window.document.createElement('link'); addedHead.async = true; addedHead.onload = addedHead.onreadystatechange = function () { if (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') { callback(); addedHead.onload = addedHead.onreadystatechange = null; } }; //And then append it to the head tag 

You can try!

0
source share

There was the same need, using a style rule definition to check if a stylesheet was loaded.

 // Load CSS dynamically. There no way to determine when stylesheet has been loaded // so we usingn hack - define `#my-css-loaded {position: absolute;}` rule in stylesheet // and the `callback` will be called when it loaded. var loadCss = function(url, cssFileId, callback){ // CSS in IE can be added only with `createStyleSheet`. if(document.createStyleSheet) document.createStyleSheet(url) else $('<link rel="stylesheet" type="text/css" href="' + url + '" />').appendTo('head') // There no API to notify when styles will be loaded, using hack to // determine if it loaded or not. var $testEl = $('<div id="' + cssFileId + '" style="display: none;"></div>').appendTo('body') var checkIfStyleHasBeenLoaded = function(){ if($testEl.css('position') == 'absolute'){ $testEl.remove() callback() }else setTimeout(checkIfStyleHasBeenLoaded, 10) } setTimeout(checkIfStyleHasBeenLoaded, 0) } 
0
source share

All Articles