Webkit - 动态创建样式表 - 什么时候真正加载?

时间:2013-07-19 13:42:05

标签: javascript jquery dom webkit

我有一些代码(实际上不是我的,但是SlickGrid库)创建了一个<style>元素,将其插入到DOM中,然后立即尝试在document.styleSheets中找到新的样式表。采集。

在WebKit中,这有时会失败。我实际上并不知道情况如何,但这并不是一贯可重复的。我想我可以通过更改代码来解决它,所以在样式元素上的load事件之前不会检查StyleSheet对象,如下所示:

  $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();
  });

和functionThatExpectsTheStylesheet尝试找到实际的样式表对象,如下所示:

    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;
      }
    }

但有时甚至在那时,找不到样式表对象。

所以,我的问题是:

  1. load事件实际上不保证styleSheet对象可用吗?这是一个错误吗?
  2. 如果没有,是否还有其他条件我可以使用或做我只需要使用超时来做这件事?
  3. 或者我绑定load事件的方式有问题,这是我的问题吗?

4 个答案:

答案 0 :(得分:6)

遗憾的是,动态加载CSS样式表仍然是一个充满浏览器怪癖的领域。在Webkit中,<style><link>元素在加载样式表时都会fire load and error events。但是,load事件本身仅表示已加载样式表资源,而不一定是已将其添加到document.styleSheets

require-css RequireJS加载程序通过基于userAgent嗅探分支其加载机制来处理此问题(几乎不可能对功能进行检测<link>标记是否会触发其{{1}事件正确)。特别是对于Webkit,检测采用load来查找StyleSheet对象何时附加到setTimeout

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); } 事件将在Webkit上触发,但为了能够访问相应的load实例,它是不可靠的。 目前唯一正确支持样式表StyleSheet事件的引擎是Firefox 18 +。

披露:我是require-css

的撰稿人

参考文献:

答案 1 :(得分:0)

  1. 我认为load事件可能比css样式表更早触发。问题可能会发生在10次中,但仍然不是最佳做法和最干净的方式。

  2. setTimeout肯定是一个很好的方法,因为可以保证你的脚本首先加载css。但是,由于只创建了样式表的标签,表示创建它的链接,因此需要首先下载css,所以无论你使用它们的方法是什么,都绝不能保证在加载事件之前加载你的css火灾。

  3. 要100%确定一切都按正确的顺序运行,通过AJAX进行同步文件读取,或者使用辅助函数进行异步AJAX调用,检查CSS是否准备就绪(这样你就不会冻结浏览器但仍然能够检查文件是否已加载)。另一种方法是链接样式表手册:首先设置样式表,然后是HTML文件标题中的代码。

    我没有深入研究加载事件,但可能是DOMContentLoaded事件只在加载CSS时触发(对此问题的研究不足)

    在这里查看类似的问题:jQuery event that triggers after CSS is loaded?

答案 2 :(得分:0)

我没有声称有解决方案,也没有对Webkit加载时序的解释,但我最近遇到了类似的问题,但是对于JS:我需要绝对确定在我包含其他之前加载了jQuery框架我自己的库,依赖于jQuery。

然后我注意到jQuery的load事件在我预期时没有被触发。

对于它的价值,我在这里解决了我的问题:

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

你可以尝试一下!

答案 3 :(得分:0)

有同样的需要,使用样式规则的检测来检查样式表是否已加载。

// Load CSS dynamically. There's 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's 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's no API to notify when styles will be loaded, using hack to 
  // determine if it's 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)
}