样式表何时添加到 document.styleSheets

发布于 2024-12-14 10:05:27 字数 1540 浏览 1 评论 0原文

我正在尝试使用 javascript 动态添加 css 样式表规则,例如示例 2 此处

它在大多数情况下都有效,但似乎存在竞争条件,导致它有时在(至少)Chrome(15.0.874 和 17.0.933)中失败。当缓存为空(或已被清除)时,这种情况很少发生。

这就是我能够缩小范围的内容。首先,我通过将外部样式表附加到 来加载它,然后创建一个新的样式表(我将在其中添加规则)。然后,我打印 document.styleSheets 的长度(立即和 1 秒后)。

$(function() {
    // it doesn't happen if this line is missing.
    $("head").append('<link rel="stylesheet" type="text/css"'+
                     'href="/css/normalize.css" />');

    var stylesheet = document.createElement("style");
    stylesheet.setAttribute("type", "text/css");
    document.getElementsByTagName('head')[0].appendChild(stylesheet);

    var b = $('body');
    b.append(document.styleSheets.length).append('<br/>');
    setTimeout(function() {
        b.append(document.styleSheets.length).append('<br/>');
    }, 1000);
});

(在 http://jsfiddle.net/amirshim/gAYzY/13/ 玩)

当缓存清除时,它有时会打印 2 然后 4 (jsfiddle 添加它自己的 2 个 css 文件),这意味着它不会添加任何样式表document.styleSheets 立即...但可能等待外部文件加载。

这是预期的吗?

如果是这样,MDN 上的示例 2(以及其他许多示例)是否损坏?由于第 27 行:

var s = document.styleSheets[document.styleSheets.length - 1];

可能会用 document.styleSheets.length == 0 进行评估

请注意,当我不先加载外部 CSS 文件时,不会发生这种情况。

I'm trying to dynamically add a css stylesheet rule using javascript, something like example 2
here.

It works most of the time, but there seems to be a race condition that makes it fail sometimes in (at least) Chrome (15.0.874 and 17.0.933). It happens infrequently when the cache is empty (or has been cleared).

Here's what I've been able to narrow it down to. First I load an external stylesheet by appending it to <head> and then I create a new stylesheet (where I would add rules). I then print the length of document.styleSheets (immediately and after 1 second).

$(function() {
    // it doesn't happen if this line is missing.
    $("head").append('<link rel="stylesheet" type="text/css"'+
                     'href="/css/normalize.css" />');

    var stylesheet = document.createElement("style");
    stylesheet.setAttribute("type", "text/css");
    document.getElementsByTagName('head')[0].appendChild(stylesheet);

    var b = $('body');
    b.append(document.styleSheets.length).append('<br/>');
    setTimeout(function() {
        b.append(document.styleSheets.length).append('<br/>');
    }, 1000);
});

(play with it at http://jsfiddle.net/amirshim/gAYzY/13/)

When the cache is clear, it sometimes prints 2 then 4 (jsfiddle adds it's own 2 css files), meaning it doesn't add either of the style sheets to document.styleSheets immediately... but probably waits for the external file to load.

Is this expected?

If so, is Example 2 on MDN (and many others out there) broken? Since line 27:

var s = document.styleSheets[document.styleSheets.length - 1];

might evaluate with document.styleSheets.length == 0

Note that this doesn't happen when I don't load the external CSS file first.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

零度℉ 2024-12-21 10:05:27

如果 JavaScript 位于 CSS 下面的页面中(几乎总是如此),则 HTML 解析器必须等待 JS 执行,直到 JS 和 CSS 完全加载并解析,因为 JS 可能会请求样式信息(Chrome 仅在以下情况下才会这样做):但脚本实际上是这样做的)。
这有效地使得外部 CSS 的加载几乎在所有情况下都被阻止。
当您稍后通过 JavaScript 插入它们或者页面中没有 JS(或者 JS 是非阻塞加载的)时,CSS 会异步加载,这意味着它们的加载和解析不会阻塞 DOM 的解析。
因此,documents.stylesheets 计数仅在工作表位于 DOM 内部后才会更新,并且仅在完全加载和解析后才会发生。

在这种情况下,可能会涉及一些时间差异。
考虑到大多数浏览器只有有限数量的管道来加载数据(有些只有两个,如 IE6,大多数有 6 个,有些甚至有 12 个,如 IE9),样式表的加载被添加到要加载的队列的末尾。
浏览器仍在加载内容,因为您在 DOMReady 上调用了函数。
这会导致样式表在一秒后未完全加载和解析,因此不会影响 document.stylesheets.length。

我在网上遇到的所有样式表示例都假设 dom 已完全解析和加载。
OffDOM 样式表甚至不允许插入或检查规则,因为它们可以具有 @import 规则,并且这些规则必须从外部加载,因此对于浏览器来说,很难确定何时可以安全地与表单交互,除非它们已完全加载并解析。
OffDOM 样式表确实公开了一个空的工作表属性,但在工作表添加到 DOM 之前不会让您与其交互。

我总是发现动态插入样式表并执行该表单中的所有更改并保留 document.stylesheets 更好。
这样做的一个很大的优点是,当您覆盖具有相同特异性的样式时,您不会因为插入错误的工作表而遇到麻烦。
由于 document.stylesheets 是一个活动的 nodeList,因此 document.stylesheets[ 2 ] 可以在每次调用该函数时指向另一个工作表(除非存储在 var 中)。
所以我倾向于使用动态插入的工作表并且只对该工作表进行操作。

If JavaScript is in the page below the CSS (which it almost always is) the HTML parser must wait with JS execution until the JS and CSS is fully loaded and parsed because of the fact that JS might request styling information (Chrome only does so when the script actually does this though).
This effectively makes the loading of external CSS blocking in almost all cases.
When you insert them later via JavaScript or there are no JS' in the page (or the JS is loaded non-blocking) CSS loads asynchronously meaning they're loaded and parsed without blocking the parsing of the DOM.
Therefor the documents.stylesheets count only gets updated after the sheet is inside the DOM and that only happens after it is fully loaded and parsed.

In this situation there might be some timing differences involved.
Considering most browsers only have a limited number of pipes through which they load data (some have only two like IE6, most have 6 and some even have 12 like IE9) the loading of the stylesheet is added at the end of the queue to be loaded.
The browser is still loading things because you call you function on DOMReady.
Which results in the stylesheet not being fully loaded and parsed one second later, so not affecting the document.stylesheets.length.

And all stylesheet examples i've run into on the web assume the dom is fully parsed and loaded.
OffDOM stylesheets don't even allow rules to be inserted or checked due to the fact that they can have @import rules and those have to be loaded externally, so for browsers it's pretty hard to determine when its safe to interact with the sheet unless they're fully loaded and parsed.
OffDOM stylesheets do expose an empty sheet property but won't let you interact with it until the sheet has been added to the DOM.

I've always found it better to insert a stylesheet dynamically and execute all changes in that one sheet and leaving the document.stylesheets alone.
This has the great advantage that when you override styles with the same specificity you wont run into trouble because of insertion in the wrong sheet.
Since document.stylesheets is a live nodeList, document.stylesheets[ 2 ] can point to another sheet every time you call the function (unless stored in a var).
So I tend to use a dynamically inserted sheet and only operate on that one.

梦毁影碎の 2024-12-21 10:05:27

这应该有帮助:'样式表何时真正加载'

至于“示例2”。如果调用 addStylesheetRules() 时加载样式表,它会中断,当然这是在 Chrome 中。

This should help: 'When is a stylesheet really loaded'

As for 'Example 2'. It will break if there is stylesheet loading when you call addStylesheetRules(), of course that is in Chrome.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文