检查外部样式表是否已加载以备用

6

已经有一个类似的问题,但我觉得它没有很好地回答我的问题。

我想要检查外部样式表是否已经加载。例如,如果由于网络问题而阻塞,则我想要知道并加载备用方案。这是jQuery主题CSS的CDN的后备方案,但我希望这不会成为特定的问题,因为我还有其他使用外部CSS的内容也想应用这个方法。

  • 引用的link元素将出现在页面上,因此不可接受模式匹配头部中链接元素的存在。
  • 我无法更改样式表中的CSS,因此添加虚拟CSS是不可接受的。
  • 由于我无法控制外部样式表中的CSS,因此依赖CSS中的现有类是脆弱的,因此我希望有其他解决方案。
  • 使用超时是否是唯一的方法?我应该设置多长时间?选择哪个事件来测量时间?
  • 我更喜欢纯JavaScript,然后是jQuery,但对其他JavaScript框架的答案也将被接受,因为它们无疑对某些人有帮助。

编辑:一个新的想法。如果样式表是通过AJAX加载的,那么检查状态码呢?这是一个可行的选择吗?有人知道吗?


这是我目前拥有的脚本:

<script type="text/javascript">
  var cdn = 'http://code.jquery.com/ui/1.10.1/themes/black-tie/jquery-ui.min.css';
  var ss = document.styleSheets;
  for (var i = 0, max = ss.length; i < max; i++) {
    var sheet = ss[i];
    var rules = sheet.rules ? sheet.rules : sheet.cssRules;
    if (sheet.href == cdn) {
      if ( rules == null || rules.length == 0) {
        var link = document.createElement("link");
        link.rel = "stylesheet";      
        link.href = '/js/jquery-ui/1.10.1/themes/black-tie/jquery-ui.min.css';
        document.getElementsByTagName("head")[0].appendChild(link);  
      }
      break;
    }  
  }
</script>

我在使用控制台(Chrome v25.0.1364.172)时发现,即使样式表已经从CDN加载完毕,document.styleSheets[x].rules仍然是null。虽然我不确定我在控制台中是否正确地访问它,也许有另一个函数或对象可以使用?

CSSStyleSheet {rules: null, cssRules: null, ownerRule: null, media: MediaList, title: null}
cssRules: null
disabled: false
href: "http://code.jquery.com/ui/1.10.1/themes/black-tie/jquery-ui.min.css"
media: MediaList
ownerNode: link
ownerRule: null
parentStyleSheet: null
rules: null
title: null
type: "text/css"
__proto__: CSSStyleSheet

如果有人能帮我找到如何检查样式表是否已经加载的方法,那将非常有帮助并且感激不尽。


1
在你的情况下,你想知道表格是否已经加载,而你链接的帖子只是处理如果头部不存在,则在头部放置<link>。不幸的是,即使通过JS创建它,也没有通过Javascript检查<link>元素是否已完成加载的方法。我确实想出了这个:http://blog.rjzaworski.com/2011/08/firing-events-when-a-stylesheet-finishes-loading/,这有点hackish,但简而言之,它涉及在页面上放置一个元素,其属性会在CSS加载后更改,然后定期检查它。 - Brian
@Brian 谢谢你的回复,这是一个有趣的方法。我认为你应该把它作为答案添加进去,这样我(和其他人)至少可以给你一些赞扬来表达感谢。你太谦虚了 :) - ian
这并不是一个完整的解决方案,因为没有真正的方法可以知道CSS何时加载到浏览器。因此,您必须在一定的“超时”时间之后设置一个相对任意的时间,然后决定使用备用CSS。这些问题通常通过负载均衡器和CDN来处理,而不是由前端JS代码处理。我认为我在这里提供的内容还不足以作为“答案”,而只是一个指向您可能想要使用JS代码解决此问题的一般方向的指针 ;) - Brian
@Brian,我终于让它工作了,使用了你博客文章中的一些想法版本。非常感谢你,我真的很感激。 - ian
1个回答

5

注意:

请在rack-jquery_ui-themes库的github repo中查看生产代码。忽略所有常量内插和大写符号,例如:THEME,它们在其他地方被替换。只需将其视为原始JavaScript即可。

成功之处

非常感谢评论区的Brian为此提供了主要灵感来源。

<meta id='rack-jquery-ui-themes-fallback-beacon' />
<script type="text/javascript">
  var meta = $("#rack-jquery-ui-themes-fallback-beacon");
  meta.addClass("ui-icon");
  if ( meta.css('width') != "16px" ) {
    $('<link rel="stylesheet" type="text/css" href="/js/jquery-ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/#{JQUERY_UI_THEME_FILE}" />').appendTo('head');
  }
  meta.remove();
</script>

添加一个meta标签,然后应用jQuery UI样式表中的CSS类,然后检查是否已更改并添加回退链接(如果未更改),然后删除meta标签。

我们在头部使用meta标签而不是在正文中使用div有其原因。大多数CSS文件都是由head中的链接元素包含的。如果检查加载的脚本紧接在其后,由于它处于head的上下文中,正文尚未加载到DOM中。在此上下文中访问document.body会导致空引用错误。

没有起作用的方法

使用AJAX进行HEAD请求并检查状态:

<script type="text/javascript">
  $.ajax({
    type: "HEAD",
    url: ':CDNURL',
    success: function(css,status) {
      // do nothing
    },
    error: function(xhr,status,error) {
      var link = document.createElement("link");
      link.rel = "stylesheet";     
      link.href = ':FALLBACK_URL';
      document.getElementsByTagName("head")[0].appendChild(link); 
    }
  });
</script>

我发现这可能是一个安全威胁,因此浏览器不允许它。

检查DOM中的样式表规则:

<script type="text/javascript">
  var has_jquery_rules = false;
  var i = document.styleSheets.length - 1;
  while (i >= 0 ) {
    var sheet = document.styleSheets[i];
    if(sheet.href == ":CDNURL/ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/jquery-ui.min.css") {
      var rules = sheet.rules ? sheet.rules : sheet.cssRules;
      has_jquery_rules = rules.length == 0 ? false : true;
      break; // end the loop.
    }
    has_jquery_rules = false;
    i--;
  }
  if ( has_jquery_rules == false ) {
    $('<link rel="stylesheet" type="text/css" href="/js/jquery-ui/#{JQueryUI::JQUERY_UI_VERSION}/themes/:THEME/#{JQUERY_UI_THEME_FILE}" />').appendTo('head');
  }
</script>

这种方法不起作用,因为一些(或全部?)浏览器会阻止访问外部样式表添加的规则。

谢谢!为什么你使用了一个meta标签而不是将一个隐藏的div添加到body中呢? - Sean
@Sean 如果当时有一个好的理由,我现在也记不起来了!我相信最近有更好的解决方法,我看到了一些库可以管理它。我也不记得在哪里……我的大脑正在慢慢融化(正如它应该的那样:) - ian
我想我弄清楚了。大多数CSS文件都是通过头部的链接元素包含的。如果您检查它是否已加载的脚本紧随其后,由于它在头部的上下文中,因此正文尚未加载到DOM中。因此,document.body实际上会给您一个空引用错误。 - Sean
1
谢谢@Sean,你真的应该因为那个评论而获得声望。优秀的侦探工作应该得到回报! - ian
1
感谢@iain。我编辑了答案并包含了那个。不确定是否会因此获得任何信誉。 :) - Sean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接