8

我有一些代码(实际上不是我的,而是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. 还是我绑定加载事件的方式有问题,这是我的问题?
4

4 回答 4

6

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

require-css RequireJS 加载器通过基于 userAgent 嗅探分支其加载机制来处理此问题(几乎不可能检测<link>标签是否会load正确触发其事件)。特别是对于 Webkit,检测求助于使用setTimeout来查找StyleSheet对象何时已附加到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);
}

因此,虽然该load事件将在 Webkit 上触发,但对于能够访问相应StyleSheet实例的目的而言,它是不可靠的。目前唯一load正确支持样式表事件的引擎是 Firefox 18+。

披露:我是 require-css 的贡献者

参考:

于 2013-07-24T16:35:57.797 回答
0
  1. 我认为加载事件可能比 CSS 样式表更早触发。这个问题可能会发生 10 次中的 1 次,但它仍然不是最佳实践和最干净的方式。

  2. setTimeout 肯定是一个好方法,因为它可以保证你的脚本首先加载 css。但是,由于只有样式表的标记,意味着创建了指向它的链接,因此需要首先下载 css,因此无论您使用这两种方法中的哪一种方法,都不能 100% 保证您的 css 在加载事件之前被加载火灾。

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

我没有深入研究加载事件,但可能是 DOMContentLoaded 事件仅在加载 CSS 时触发(对此进行了不良研究)

在这里寻找类似的问题:加载 CSS 后触发的 jQuery 事件?

于 2013-07-19T13:54:17.300 回答
0

我没有声称有解决方案,也没有解释 Webkit 加载时间,但我最近遇到了类似的问题,但使用 JS:我需要绝对确定 jQuery 框架已加载,然后才包含我的其他库自己的,这取决于jQuery。

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

对于它的价值,我是如何解决我的问题的:

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

你可以试一试!

于 2013-07-22T14:52:04.567 回答
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)
}
于 2013-11-29T21:07:23.653 回答