有没有办法检测CSS文件何时完全加载?

41
我一直在测试很多JavaScript和CSS的懒加载器,它们会插入<script> 和 <link>标签去加载文件。 然而问题是,<link>标签不会触发onload事件,所以很难检测它们何时已经加载完成。我找到的唯一解决方法是在一个虚拟元素上设置display: none;(在要加载的CSS文件中),并轮询该元素以检查它何时被设置为 display:none。但是,这种方法除了丑陋之外,当然只适用于单个CSS文件。
所以我想知道:是否有其他方法可以检测CSS文件是否已加载完成?

2
请查看此链接:https://github.com/jAndreas/Supply - jAndy
我希望有一种非XHR解决方案,这样我加载的文件就可以被缓存,总体上感觉更加清洁,可以添加/删除文件而不是加载CSS/JS并插入/执行它... - Lukas
这是我所知道的唯一可靠的方法。将数据流传输到客户端并将其放入“style”标签中。 - jAndy
1
(关于问题中的注释,我已经删除了,关于“页面底部”的答案。Stack Overflow 经常重新排列答案,因此无论哪个答案在底部都会经常更改。)关于程序设计的相关内容。请仅返回翻译后的文本。 - halfer
10个回答

17

编辑:值得注意的是,自我初始回答以来,浏览器对CSS文件的onload事件的支持已经改进。尽管它还没有完全得到支持,但下面的答案仍然有一定的相关性。这里是一个兼容性图表,不确定这个来源的可靠性。

好的,我终于找到了解决方案。

这个人http://tugll.tugraz.at/96784/weblog/9080.html插入链接标签并轮询document.styleSheets[index].rules,直到它不再是undefined(其中index当然是新插入文件的索引)。不幸的是,他的代码有缺陷,只能在Safari和Firefox上运行。因此,我修复了错误,添加了Opera和Internet Explorer的功能,甚至添加了为添加多个CSS和JS文件以及1个最终回调(当所有文件都加载完成时)的特性。结果可以在这里找到:

https://github.com/LukasBombach/Lazyloader


6
值得知道的是,现在的Chrome和Firefox版本会在链接上触发onload事件。
var cssFile = document.createElement("link");
cssFile.setAttribute("rel", "stylesheet");
cssFile.setAttribute("type", "text/css");
// Add event listener
cssFile.onload = function(){ console.log('loaded'); }
cssFile.setAttribute("href", 'pathToYour.css');
document.getElementsByTagName("head")[0].appendChild(cssFile);

onload事件会检测样式表何时完成加载,但在CSS开始应用之前触发。 - snazzybouche

4

编辑:(由于可能不支持 WebKit)

因此,我更愿意推荐JQuery LOADER

$("a.button, input.button, button.button").Loader(
{
    url: [
        'core.css',
        'theme.css',
        'button.css'
    ],
    success: function() {
        $(this).button();
    }
});

你可以查看LazyLoad JavaScript库。

LazyLoad是一个微小的(仅1,541字节压缩),无依赖的JavaScript库,可轻松按需加载外部JavaScript和CSS文件(此版本中新增)。它非常适合在页面余下内容加载完毕后或按需加载时快速、不显眼地加载大型外部脚本和样式表。

除了CSS支持,这个版本的LazyLoad还添加了对在支持此功能的浏览器中并行加载多个资源的支持。要并行加载多个资源,只需在单个LazyLoad调用中传递URL数组即可。


2
我不会依赖于这个:// Gecko和WebKit不支持链接节点上的onload事件,所以我们只能在短暂的延迟后完成并希望一切顺利。 - jAndy
@jAndy:那真的是个大问题吗?只有一行代码需要更改;)如果您使用jQuery,可以使用..ready()。 - eriksv88
1
@sv88erik:我不明白你的意思。我只是从源代码中复制了那个评论,基本上告诉我们,这个脚本根本没有提供解决问题的方法。 - jAndy
@jAndy:啊,我明白你的意思了!但是很容易通过使用jQuery来解决这个问题。 - eriksv88
@sv88erik:我不知道jQuery怎么能帮助到这里的任何人。 - jAndy
显示剩余3条评论

3
尝试在文件末尾添加某个CSS规则,并等待CSS被应用(通过JavaScript检查)。之后,您可以相当确定CSS已被加载。但是我没有经验。这只是一个快速的想法,值得一试。

问题在于,如果您有X个CSS文件,则需要X个不同的CSS规则,这很混乱。此外,您需要管理许多CSS文件<->CSS规则关系,并且可能会用尽CSS规则。 - Lukas
我同意这一点。最舒适的实现方式可能是编写一个JS函数来加载CSS文件。由于检查规则是以CSS文件名命名的,所以JS函数可以自行解决规则。但是我现在没有时间给你一个简短的例子。 - Daniel Böhmer

2

这是目前我发现的最简单和最好的解决方案,它完美地工作:

var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = css_url;

head.appendChild(link);

var img = document.createElement('img');
document.body.appendChild(img);

img.onerror = img.onload = function() {
    img.onerror = img.onload = null;
    document.body.removeChild(img);
    // do whatever you need to do when css loaded;
};

img.src = css_url;

2
尝试这个: 1- 在头部预加载CSS。
<link rel="preload" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" as="style" onload="this.rel='stylesheet'"> 
<link rel="preload" href="style.css" as="style" onload="this.rel='stylesheet'"> 

2 - 在页脚预加载js文件

    <link rel="preload" as="script" href="https://ajax.googleapis.com/ajax/libs/jquery/2.2.1/jquery.min.js">
    <link rel="preload" as="script" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">
    <link rel="preload" as="script" href="script.js">

在编程中,将 </body> 标签前添加以下内容。
<script>        
/**  load defer css - js*/  
// defer css
var cssAll = document.querySelectorAll('[as="style"]');
for(i = 0; i < cssAll.length; i++){
    if(cssAll[i].getAttribute("rel") == "preload"){         
        cssAll[i].setAttribute("rel", "stylesheet");
    }
}
//defer js
var jsAll = document.querySelectorAll('[as="script"]');
if(!window.jQuery)
{
    // load jquery lib
    var script = document.createElement('script');
    script.type = "text/javascript";
    script.src = jsAll[0].getAttribute("href");
    document.getElementsByTagName('head')[0].appendChild(script);
   // load other lib after load jquery
    script.onload = function () {
        for(i = 1; i < jsAll.length; i++){
            var jsNext = document.createElement('script');
            jsNext.type = "text/javascript";
            jsNext.src = jsAll[i].getAttribute("href");
            document.getElementsByTagName('head')[0].appendChild(jsNext);
        }
    }   
}
</script>   

我在Firefox 57、Chrome 61和Yandex上进行了测试,但没有在Opera和IE上进行测试。


2
我想提供一些关于可能发生了什么变化的见解。
根据Mozilla有关<link>的文档中的最后一个段落,可以在FF版本9及以上以及Chrome 19及以上的浏览器中将加载事件附加到链接元素。IE和Safari没有被提及。https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link

1
使用类似于 Supply 的脚本加载器,代码如下所示:

supply.listen('text/css', function(payload, filename) {
    switch(filename) {
        case: 'foo.css': {
            // do something
            break;
        }
        case: 'baseball.css': {
            break;
        }
        // ...
    }
});

supply.files({
    stylesheet: [
        'foo.css',
        'baseball.css'
    ]
});

参考:SupplyJS


1
关于“加载检查CSS规则”:
如果在您的JS脚本中,您在头部添加一个包含简单规则的样式标签,例如:#loadingCss { display: block; }
然后,您只需在所有CSS文件中添加以下内容:#loadingCss { display: none; }
在文档的头部添加样式标签之前添加链接标签。这样,CSS文件中的CSS规则将更为重要(相同选择器->相同优先级,文档中最后一个是赢家)。
然后,在您的JS脚本中,您只需检查#loadingCss的可见性。一旦您知道CSS文件已加载,就可以删除样式标签。
对于您想要加载的下一个CSS文件,您可以再次在头元素的末尾添加此样式标签。
这样,只需要一个规则,您就可以管理所有CSS文件的加载。这种解决方案唯一的问题是:您不能同时加载多个CSS文件。但我相信有办法解决这个问题。
无论如何,我不确定这种解决方案(使用CSS规则来检查加载)是否是一个非常好的解决方案。也许还有其他方法来解决这个问题。

0
我之前使用了一个 tailwindcss 的 CDN -> <script src="https://cdn.tailwindcss.com"></script>。现在,我把它放在了 body 标签的最后,但是我想先加载 CSS,所以我只需要在 head 标签中添加上述脚本,在 body 加载之前就可以了。

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