如何确定CSS是否已加载?

29

如何在Watin 2.1中断言页面的CSS已成功加载并应用其样式?


尝试使用以下代码:https://dev59.com/cnE85IYBdhLWcg3w3Xn6 - yAnTar
@yAnTar - 我不确定那有什么帮助..我不想在我的页面中放置一堆仅用于测试的ajax代码。 - Erik Funkenbusch
5个回答

32

在做了一些研究并撰写完我的答案之后,我偶然发现了这个链接,它解释了你需要了解的关于CSS加载时间和如何检查CSS的所有内容。

实际上,提供的链接解释得非常好,以至于我将引用其中的一些内容作为日后的参考。
如果您感兴趣,我的回答本来是第2种方法和第4种方法的变体。

什么时候样式表真正被加载?

...

有了这个基础知识,让我们来看看这里有什么。

// my callback function 
// which relies on CSS being loaded function
CSSDone() {
    alert('zOMG, CSS is done');
};

// load me some stylesheet 
var url = "http://tools.w3clubs.com/pagr/1.sleep-1.css",
    head = document.getElementsByTagName('head')[0],
    link = document.createElement('link');

link.type = "text/css"; 
link.rel = "stylesheet";
link.href = url;

// MAGIC 
// call CSSDone() when CSS arrives
head.appendChild(link);

对于神奇部分的选项,按从简单到荒谬排序:

  1. 监听link.onload事件
  2. 监听link.addEventListener('load')事件
  3. 监听link.onreadystatechange事件
  4. 使用setTimeout并检查document.styleSheets中的更改
  5. 使用setTimeout并检查您创建但使用新CSS样式的特定元素的样式更改

第五个选项太疯狂了,并假设您可以控制CSS的内容,因此请忘记它。此外,它会在超时期间检查当前样式,这意味着它将刷新重排队列,并且可能会潜在地变慢。 CSS到达越慢,回流就越多。 所以,真的,请忘记它。

那么,如何实现魔法?

// MAGIC 

// #1   
link.onload = function () {
    CSSDone('onload listener');
};   

// #2   
if (link.addEventListener) {
    link.addEventListener('load', function() {
        CSSDone("DOM's load event");
    }, false);   
};   

// #3   
link.onreadystatechange = function() {
    var state = link.readyState;
    if (state === 'loaded' || state === 'complete') {
        link.onreadystatechange = null;
        CSSDone("onreadystatechange");
    }   
};

// #4   
var cssnum = document.styleSheets.length;
var ti = setInterval(function() {
    if (document.styleSheets.length > cssnum) {
        // needs more work when you load a bunch of CSS files quickly
        // e.g. loop from cssnum to the new length, looking
        // for the document.styleSheets[n].href === url
        // ...

        // FF changes the length prematurely :(
        CSSDone('listening to styleSheets.length change');
        clearInterval(ti);
    }   
}, 10);

// MAGIC ends

请检查我的答案,该答案涉及到@ShowScripter链接文章的重要后续更新。 - JohnSz

13

与@ShadowScripter链接的文章已更新。新的方法据称可在所有浏览器中运行,包括FF。

var style = document.createElement('style');
style.textContent = '@import "' + url + '"';

var fi = setInterval(function() {
  try {
    style.sheet.cssRules; // <--- MAGIC: only populated when file is loaded
    CSSDone('listening to @import-ed cssRules');
    clearInterval(fi);
  } catch (e){}
}, 10);  

document.getElementsByTagName('head')[0].appendChild(style);

2
页面加载后,您可以验证一些元素的样式,类似这样:
var style = browser.Div(Find.ByClass("class")).Style;
Assert.That(Style.Display, Is.StringContaining("none"));
Assert.That(Style.FontSize, Is.EqualTo("10px"));

And etc...


好的,但这只是问题的一部分。这只验证了样式是否应用,而不是所有样式都已有效加载。例如,我如何确保给定的背景图像作为样式的一部分已加载?检查样式将显示样式具有背景图像,但我如何验证该图像已加载并正在显示? - Erik Funkenbusch
您可以验证元素属性等等...可见性测试只能通过肉眼完成。如果您有解决方案,我很乐意知道答案 :) - alonp

1
由于浏览器兼容性可能有所不同,且新的未来浏览器标准也可能会发生变化,因此如果您使用单个样式表,则建议结合onload监听器和添加CSS到样式表中,以便在HTML元素z-index更改时进行监听。否则,请使用下面的函数,并为每种样式添加一个新的meta标签。
请将以下内容添加到您正在加载的CSS文件中:
#*(insert a unique id for he current link tag)* {
    z-index: 0
}

将以下内容添加到您的脚本中:

function whencsslinkloads(csslink, whenload ){
    var intervalID = setInterval(
        function(){
            if (getComputedStyle(csslink).zIndex !== '0') return;
            clearInterval(intervalID);
            csslink.onload = null;
            whenload();
        },
        125 // check for if it has loaded 8 times a second
    );
    csslink.onload = function(){
        clearInterval(intervalID);
        csslink.onload = null;
        whenload();
    }
}

例子

index.html:

<!doctype html>
<html>
    <head>
        <link rel=stylesheet id="EpicStyleID" href="the_style.css" />
        <script async href="script.js"></script>
    </head>
    <body>
        CSS Loaded: <span id=result>no</span>
    </body>
</html>

script.js:

function whencsslinkloads(csslink, whenload ){
    var intervalID = setInterval(
        function(){
            if (getComputedStyle(csslink).zIndex !== '0') return;
            clearInterval(intervalID);
            csslink.onload = null;
            whenload();
        },
        125 // check for if it has loaded 8 times a second
    );
    csslink.onload = function(){
        clearInterval(intervalID);
        csslink.onload = null;
        whenload();
    }
}
/*************************************/
whencsslinkloads(
    document.getElementById('EpicStyleID'),
    function(){
        document.getElementById('result').innerHTML = '<font color=green></font>'
    }
)

the_style.css

#EpicStyleID {
    z-index: 0
}

请不要使您的脚本同步加载(没有async属性),只是为了捕获链接的onload事件。有更好的方法,如上面的方法。

1
只需检查CSS变量是否存在
var blnCSSLoaded = false;
var iCSSLoad = setInterval(function() {
    var bodyStyles = window.getComputedStyle(document.body);
    var fooBar = bodyStyles.getPropertyValue('--blnMyCSSInit');
    if(fooBar) {
        clearInterval(iCSSLoad);
        blnCSSLoaded = true;
    }
}, 200);

你必须在CSS中这样定义变量
:root {
  --blnMyCSSInit: #0077bf;
}

我喜欢使用CSS自定义属性的想法。如果我没记错的话,与@jack-G的回答相比,这种方法的一个重要优势是自定义属性实际上不需要以任何方式在DOM元素上设置样式。 - shawn caza

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