Когда добавлена ​​стильная таблица в document.styleSheets

Я пытаюсь динамически добавить правило CSS CSS, используя javascript, что-то вроде примера 2 здесь.

Он работает большую часть времени, но, похоже, это состояние гонки, которое иногда приводит к поломке (по крайней мере) Chrome (15.0.874 и 17.0.933). Это происходит нечасто, когда кеш пуст (или был очищен).

Вот что я смог сузить. Сначала я загружаю внешнюю таблицу стилей, добавляя ее к <head>, а затем создаю новую таблицу стилей (где я бы добавил правила). Затем я печатаю длину 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 сразу... но, вероятно, ожидает загрузки внешнего файла.

Ожидается ли это?

Если да, то Пример 2 в MDN (и многие другие там) сломан? Поскольку строка 27:

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

может оцениваться с помощью document.styleSheets.length == 0

Обратите внимание, что это не происходит, когда я не загружаю внешний файл CSS сначала.

Ответ 1

Если JavaScript находится на странице ниже CSS (что почти всегда есть), парсер HTML должен ждать с выполнением JS до тех пор, пока JS и CSS не будут полностью загружены и проанализированы из-за того, что JS может запрашивать информацию о стиле (только для Chrome делает это, когда script действительно делает это, хотя). Это фактически делает загрузку внешней блокировки CSS практически во всех случаях. Когда вы вставляете их позже через JavaScript или нет JS 'на странице (или JS загружается без блокировки), CSS загружает асинхронно, то есть они загружаются и анализируются без блокировки разбора DOM. Поэтому количество экземпляров documents.stylesheet только обновляется после того, как лист находится внутри DOM, и это происходит только после полной загрузки и анализа.

В этой ситуации могут быть некоторые временные различия. Учитывая, что большинство браузеров имеют ограниченное количество каналов, через которые они загружают данные (некоторые из них имеют только два типа IE6, большинство из них имеют 6, а некоторые даже имеют 12, как IE9), загрузка таблицы стилей добавляется в конец загружаемой очереди, Браузер по-прежнему загружает вещи, потому что вы вызываете функцию DOMReady. Это приводит к тому, что таблица стилей не будет полностью загружена и проанализирована на одну секунду позже, поэтому не влияет на document.stylesheets.length.

И все примеры стилей, с которыми я столкнулся в Интернете, предполагают, что dom полностью разбирается и загружается. Таблицы стилей OffDOM даже не позволяют вставлять или проверять правила из-за того, что они могут иметь правила @import, и их нужно загружать извне, поэтому для браузеров довольно сложно определить, когда их безопасно взаимодействовать с листом, если они не будут "полностью загружен и проанализирован. Таблицы стилей OffDOM выставляют пустое свойство листа, но не позволят вам взаимодействовать с ним, пока лист не будет добавлен в DOM.

Мне всегда было удобно вставлять таблицу стилей динамически и выполнять все изменения на этом одном листе и оставлять только те документы document.stylesheets. Это имеет большое преимущество в том, что когда вы переопределяете стили с той же специфичностью, вы не столкнетесь с проблемой из-за вставки в неправильный лист. Поскольку document.stylesheets - это live nodeList, document.stylesheets [2] может указывать на другой лист каждый раз, когда вы вызываете функцию (если не хранится в var). Поэтому я предпочитаю использовать динамически вставленный лист и работать только с ним.